├── .gitignore ├── .vscode └── launch.json ├── Cargo.toml ├── README.md ├── scripts ├── README.md ├── environment.yml ├── generate_tests.py └── templates │ └── test_py_impl_random_arrays_template.rs ├── src ├── convolutions.rs ├── lib.rs └── transposed_convolutions.rs └── tests ├── conv2d_stride1_same_automated_test.rs ├── conv2d_stride1_same_bias_automated_test.rs ├── conv2d_stride1_valid_automated_test.rs ├── conv2d_stride1_valid_bias_automated_test.rs ├── conv2d_stride2_same_automated_test.rs ├── conv2d_stride2_same_bias_automated_test.rs ├── conv2d_stride2_valid_automated_test.rs ├── conv2d_stride2_valid_bias_automated_test.rs ├── conv2d_transpose_stride1_same_automated_test.rs ├── conv2d_transpose_stride1_same_bias_automated_test.rs ├── conv2d_transpose_stride1_valid_automated_test.rs ├── conv2d_transpose_stride1_valid_bias_automated_test.rs ├── conv2d_transpose_stride2_same_automated_test.rs ├── conv2d_transpose_stride2_same_bias_automated_test.rs ├── conv2d_transpose_stride2_valid_automated_test.rs ├── conv2d_transpose_stride2_valid_bias_automated_test.rs └── npy_files ├── input_rand_same_shape.npy ├── input_rand_same_shapes.npy ├── kernel.npy ├── kernel_rand_same_shape.npy ├── out1.npy ├── output.npy ├── output_rand_same_shape.npy ├── output_simple_example.npy ├── simple_example_input.npy ├── simple_weight.npy ├── weight1.npy ├── x1.npy └── y_hat.npy /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/pycharm,python,jupyternotebooks,rust 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=pycharm,python,jupyternotebooks,rust 4 | 5 | ### JupyterNotebooks ### 6 | # gitignore template for Jupyter Notebooks 7 | # website: http://jupyter.org/ 8 | 9 | .ipynb_checkpoints 10 | */.ipynb_checkpoints/* 11 | 12 | python_test/ 13 | # IPython 14 | profile_default/ 15 | ipython_config.py 16 | 17 | # Remove previous ipynb_checkpoints 18 | # git rm -r .ipynb_checkpoints/ 19 | 20 | ### PyCharm ### 21 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 22 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 23 | 24 | # User-specific stuff 25 | .idea/**/workspace.xml 26 | .idea/**/tasks.xml 27 | .idea/**/usage.statistics.xml 28 | .idea/**/dictionaries 29 | .idea/**/shelf 30 | 31 | # Generated files 32 | .idea/**/contentModel.xml 33 | 34 | # Sensitive or high-churn files 35 | .idea/**/dataSources/ 36 | .idea/**/dataSources.ids 37 | .idea/**/dataSources.local.xml 38 | .idea/**/sqlDataSources.xml 39 | .idea/**/dynamic.xml 40 | .idea/**/uiDesigner.xml 41 | .idea/**/dbnavigator.xml 42 | 43 | # Gradle 44 | .idea/**/gradle.xml 45 | .idea/**/libraries 46 | 47 | # Gradle and Maven with auto-import 48 | # When using Gradle or Maven with auto-import, you should exclude module files, 49 | # since they will be recreated, and may cause churn. Uncomment if using 50 | # auto-import. 51 | # .idea/artifacts 52 | # .idea/compiler.xml 53 | # .idea/jarRepositories.xml 54 | # .idea/modules.xml 55 | # .idea/*.iml 56 | # .idea/modules 57 | # *.iml 58 | # *.ipr 59 | 60 | # CMake 61 | cmake-build-*/ 62 | 63 | # Mongo Explorer plugin 64 | .idea/**/mongoSettings.xml 65 | 66 | # File-based project format 67 | *.iws 68 | 69 | # IntelliJ 70 | out/ 71 | 72 | # mpeltonen/sbt-idea plugin 73 | .idea_modules/ 74 | 75 | # JIRA plugin 76 | atlassian-ide-plugin.xml 77 | 78 | # Cursive Clojure plugin 79 | .idea/replstate.xml 80 | 81 | # Crashlytics plugin (for Android Studio and IntelliJ) 82 | com_crashlytics_export_strings.xml 83 | crashlytics.properties 84 | crashlytics-build.properties 85 | fabric.properties 86 | 87 | # Editor-based Rest Client 88 | .idea/httpRequests 89 | 90 | # Android studio 3.1+ serialized cache file 91 | .idea/caches/build_file_checksums.ser 92 | 93 | ### PyCharm Patch ### 94 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 95 | 96 | # *.iml 97 | # modules.xml 98 | # .idea/misc.xml 99 | # *.ipr 100 | 101 | # Sonarlint plugin 102 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 103 | .idea/**/sonarlint/ 104 | 105 | # SonarQube Plugin 106 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 107 | .idea/**/sonarIssues.xml 108 | 109 | # Markdown Navigator plugin 110 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 111 | .idea/**/markdown-navigator.xml 112 | .idea/**/markdown-navigator-enh.xml 113 | .idea/**/markdown-navigator/ 114 | 115 | # Cache file creation bug 116 | # See https://youtrack.jetbrains.com/issue/JBR-2257 117 | .idea/$CACHE_FILE$ 118 | 119 | # CodeStream plugin 120 | # https://plugins.jetbrains.com/plugin/12206-codestream 121 | .idea/codestream.xml 122 | 123 | ### Python ### 124 | # Byte-compiled / optimized / DLL files 125 | __pycache__/ 126 | *.py[cod] 127 | *$py.class 128 | 129 | # C extensions 130 | *.so 131 | 132 | # Distribution / packaging 133 | .Python 134 | build/ 135 | develop-eggs/ 136 | dist/ 137 | downloads/ 138 | eggs/ 139 | .eggs/ 140 | parts/ 141 | sdist/ 142 | var/ 143 | wheels/ 144 | pip-wheel-metadata/ 145 | share/python-wheels/ 146 | *.egg-info/ 147 | .installed.cfg 148 | *.egg 149 | MANIFEST 150 | 151 | # PyInstaller 152 | # Usually these files are written by a python script from a template 153 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 154 | *.manifest 155 | *.spec 156 | 157 | # Installer logs 158 | pip-log.txt 159 | pip-delete-this-directory.txt 160 | 161 | # Unit test / coverage reports 162 | htmlcov/ 163 | .tox/ 164 | .nox/ 165 | .coverage 166 | .coverage.* 167 | .cache 168 | nosetests.xml 169 | coverage.xml 170 | *.cover 171 | *.py,cover 172 | .hypothesis/ 173 | .pytest_cache/ 174 | pytestdebug.log 175 | 176 | # Translations 177 | *.mo 178 | *.pot 179 | 180 | # Django stuff: 181 | *.log 182 | local_settings.py 183 | db.sqlite3 184 | db.sqlite3-journal 185 | 186 | # Flask stuff: 187 | instance/ 188 | .webassets-cache 189 | 190 | # Scrapy stuff: 191 | .scrapy 192 | 193 | # Sphinx documentation 194 | docs/_build/ 195 | doc/_build/ 196 | 197 | # PyBuilder 198 | target/ 199 | 200 | # Jupyter Notebook 201 | 202 | # IPython 203 | 204 | # pyenv 205 | .python-version 206 | 207 | # pipenv 208 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 209 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 210 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 211 | # install all needed dependencies. 212 | #Pipfile.lock 213 | 214 | # poetry 215 | #poetry.lock 216 | 217 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 218 | __pypackages__/ 219 | 220 | # Celery stuff 221 | celerybeat-schedule 222 | celerybeat.pid 223 | 224 | # SageMath parsed files 225 | *.sage.py 226 | 227 | # Environments 228 | # .env 229 | .env/ 230 | .venv/ 231 | env/ 232 | venv/ 233 | ENV/ 234 | env.bak/ 235 | venv.bak/ 236 | pythonenv* 237 | 238 | # Spyder project settings 239 | .spyderproject 240 | .spyproject 241 | 242 | # Rope project settings 243 | .ropeproject 244 | 245 | # mkdocs documentation 246 | /site 247 | 248 | # mypy 249 | .mypy_cache/ 250 | .dmypy.json 251 | dmypy.json 252 | 253 | # Pyre type checker 254 | .pyre/ 255 | 256 | # pytype static type analyzer 257 | .pytype/ 258 | 259 | # operating system-related files 260 | # file properties cache/storage on macOS 261 | *.DS_Store 262 | # thumbnail cache on Windows 263 | Thumbs.db 264 | 265 | # profiling data 266 | .prof 267 | 268 | 269 | ### Rust ### 270 | # Generated by Cargo 271 | # will have compiled files and executables 272 | /target/ 273 | 274 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 275 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 276 | Cargo.lock 277 | 278 | # End of https://www.toptal.com/developers/gitignore/api/pycharm,python,jupyternotebooks,rust 279 | 280 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: Current File", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "scripts/generate_tests.py", 12 | "console": "integratedTerminal", 13 | "justMyCode": true 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "convolutions-rs" 3 | version = "0.3.4" 4 | edition = "2018" 5 | license = "MIT" 6 | description = "Fast, minimal dependency, completely Rust implementation of convolutions for machine learning." 7 | repository = "https://github.com/Conzel/convolutions-rs" 8 | readme = "README.md" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | ndarray = "0.15.3" 14 | num-traits = "0.2.14" 15 | ndarray-npy = "0.8.1" 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # convolutions-rs 2 | convolutions-rs is a crate that provides a fast, well-tested convolutions library for machine learning written entirely in Rust with minimal dependencies. In particular, this is the first convolutions crate that has absolutely no dependencies on native C libraries. We provide both transposed convolutions (also called deconvolutions), as well as normal convolutions. 3 | 4 | This crate has been developed in the course of the ZipNet project (https://github.com/Conzel/zipnet), where we required a C-free implementation of convolutions in order to compile our code to WebASM. 5 | 6 | ## Features 7 | - [x] Minimal dependencies, especially no C-dependencies 8 | - [x] Extensively tested through randomly generated unit tests 9 | - [x] 100% compatible with Pytorch implementation 10 | - [x] Generics to ensure smooth usage 11 | - [x] Speed verified by benchmarking 12 | 13 | As of now, this crate is as fast as Pytorch on small images, but has a noticeable slowdown on large and medium images (takes ~20-50x as much time). We are still reaonsably fast enough for research/sample applications, but we aim to improve the speed to get close to PyTorch. Benchmarks can be found at https://github.com/Conzel/convolutions-rs-benchmarks/. 14 | 15 | Additionally, Transposed Convolution is currently relatively slow. We are working 16 | to alleviate the issue. 17 | 18 | ## Compatibility with implementations 19 | As mentioned in the bullet points above, we are 100% compatible with the Pytorch implementation. This doesn't mean that we support all operations that Pytorch has, but all those which we support give the same output as in Pytorch. This is verified 20 | by random array tests. 21 | 22 | The implementations of Pytorch and Tensorflow mostly agree. The only case we found where they sometimes don't agree is `same` padding, as there might be different strategies to do this. This is not a problem for strides of size 1, but it results in different array values for strides > 1. 23 | 24 | See here for further discussion: https://stackoverflow.com/questions/52975843/comparing-conv2d-with-padding-between-tensorflow-and-pytorch 25 | 26 | ## Usage 27 | As mentioned, this package provides normal convolutions as well as transposed convolutions. We provide both in the form of free functions as well as something resembling a neural network layer. This crate also requires ndarray to use the functions, as input and output are in the form of ndarrays. 28 | 29 | Example: 30 | ```rust 31 | use convolutions_rs::convolutions::*; 32 | use ndarray::*; 33 | use convolutions_rs::Padding; 34 | 35 | // Input has shape (channels, height, width) 36 | let input = Array::from_shape_vec( 37 | (1, 4, 4), 38 | vec![1.,2.,3.,4.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.] 39 | ) 40 | .unwrap(); 41 | 42 | // Kernel has shape (channels out, channels in, height, width) 43 | let kernel: Array4 = Array::from_shape_vec( 44 | (2,1,2,2), 45 | vec![1.,1.,1.,1.,1.,1.,1.,1.] 46 | ) 47 | .unwrap(); 48 | 49 | let conv_layer = ConvolutionLayer::new(kernel.clone(), 1, Padding::Valid); 50 | let output_layer: Array3 = conv_layer.convolve(&input); 51 | let output_free = conv2d(&kernel, &input, Padding::Valid, 1); 52 | 53 | println!("Layer: {:?}", output_layer); 54 | println!("Free: {:?}", output_free); 55 | ``` 56 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts 2 | This directory contains scripts there automatic test generation. 3 | 4 | ## Automated unit tests 5 | We have implemented automated unit test generation for convolution and transposed convolutions. These have to conform tightly to the tensorflow implementation (https://www.tensorflow.org/api_docs/python/tf/nn/conv2d and https://www.tensorflow.org/api_docs/python/tf/nn/conv2d_transpose respectively), which is why automatic code generation provides useful. We create random arrays, pass them through the tensorflow outputs and check the output of the Rust implementation against them. 6 | 7 | As always, tests can be run via `cargo test`. The tests can be generated through the script by simply running the script in `scripts/generate_tests.py` and modified through the file in the `templates` folder. 8 | 9 | For this, we recommend `tensorflow 2.5.0`, `pytorch 1.10.2`, `numpy 1.19.5` and `Jinja2 3.0.0`. 10 | 11 | We have provided a yaml file with a conda environment for this purpose. Install it by running `conda env create -f environment.yml` in this folder. Don't forget to activate the environment running `conda activate convolutions-rs`. -------------------------------------------------------------------------------- /scripts/environment.yml: -------------------------------------------------------------------------------- 1 | name: convolutions-rs 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - tensorflow=2.7.* 6 | - jinja2=3.* 7 | - pytorch=1.10.* 8 | -------------------------------------------------------------------------------- /scripts/generate_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import jinja2 3 | import numpy as np 4 | import itertools 5 | import os 6 | import torch 7 | 8 | 9 | class RandomArrayTest: 10 | def __init__(self, test_name, layer_name, random_test_objects): 11 | """Struct that represents one Random Array Test. 12 | test_name: str, name of the test case 13 | layer_name: str, name of the layer to test 14 | random_test_objects: [TestObject] 15 | """ 16 | assert layer_name == "ConvolutionLayer" or layer_name == "TransposedConvolutionLayer", "Layer name unknown" 17 | self.test_name = test_name 18 | self.layer_name = layer_name 19 | if self.layer_name == "ConvolutionLayer": 20 | self.function_name = "convolve" 21 | elif self.layer_name == "TransposedConvolutionLayer": 22 | self.function_name = "transposed_convolve" 23 | self.random_test_objects = random_test_objects 24 | 25 | 26 | class RandomArrayTestObject: 27 | def __init__(self, input_arr, kernel, output_arr, bias, padding, stride=1): 28 | """Struct that represents one test case for the random array tests. 29 | input_arr: ndarray, 3-Dimensional floating point numpy array 30 | output_arr: ndarray, 3-Dimensional floating point numpy array 31 | kernel: ndarray, 3-Dimensional floating point numpy array, weights of 32 | the convolutional layer 33 | stride: int 34 | padding: str, valid or same. Valid padding just applies the kernel directly, 35 | same padding ensures that inputsize = outputsize 36 | """ 37 | if padding == "VALID": 38 | self.padding = "Padding::Valid" 39 | elif padding == "SAME": 40 | self.padding = "Padding::Same" 41 | else: 42 | raise ValueError(f"Illegal padding value {padding}") 43 | 44 | self.input_arr = numpy_array_to_rust(input_arr, shape_vec=True) 45 | self.output_arr = numpy_array_to_rust(output_arr, shape_vec=True) 46 | self.kernel = numpy_array_to_rust(kernel, shape_vec=True) 47 | if bias is None: 48 | self.bias = "None" 49 | else: 50 | self.bias = numpy_array_to_rust( 51 | bias, shape_vec=True, bias=True) 52 | self.stride = stride 53 | 54 | 55 | def numpy_array_to_rust(x, shape_vec=False, bias=False): 56 | """ 57 | Outputs a numpy array as a Rust ndarray. 58 | If shape_vec is set to true, outputs 59 | the array creationg through the shape_vec Rust function. 60 | The Rust array macro seems broken for 4-D arrays, so this is a 61 | workaround. 62 | """ 63 | # This removes the "dtype=..." info in the representation, 64 | # if needed 65 | if x.dtype == np.float64: 66 | ending_delimiter = -1 67 | elif x.dtype == np.float32: 68 | ending_delimiter = -16 69 | else: 70 | raise ValueError("array has an unsupported datatype: {x.dtype}") 71 | 72 | if shape_vec: 73 | x_shape = x.shape 74 | x = x.flatten() 75 | # removes leading array and closing paren tokens 76 | array_repr = f"{repr(x)}"[6:][:ending_delimiter].replace("\n", "\n\t\t") 77 | if shape_vec: 78 | if bias: 79 | return f"Some(Array::from_shape_vec({x_shape}, vec!{array_repr}).unwrap())" 80 | else: 81 | return f"Array::from_shape_vec({x_shape}, vec!{array_repr}).unwrap()" 82 | else: 83 | return f"array!{array_repr}".rstrip().rstrip(",") 84 | 85 | 86 | def torch_to_tf_img(x): 87 | return np.moveaxis(x, 0, 2) 88 | 89 | 90 | def tf_to_torch_img(x): 91 | return np.moveaxis(x, 2, 0) 92 | 93 | 94 | def tf_to_torch_ker(k): 95 | return np.moveaxis(k, [2, 3], [1, 0]) 96 | 97 | 98 | def tf_to_torch_ker_transpose(k): 99 | return np.moveaxis(k, [2, 3], [0, 1]) 100 | 101 | 102 | def transform_img(orig, dest, x): 103 | """Transforms img between orig and dest data formats. 104 | 105 | orig: str, tf, rust, or pt 106 | dest: str, tf, rust, or pt 107 | x: ndarray to transform 108 | """ 109 | return transform(orig, dest, x, is_kernel=False) 110 | 111 | 112 | def transform_ker(orig, dest, x): 113 | """Transforms ker between orig and dest data formats. 114 | 115 | orig: str, tf, rust, or pt 116 | dest: str, tf, rust, or pt 117 | x: ndarray to transform 118 | """ 119 | return transform(orig, dest, x, is_kernel=True) 120 | 121 | 122 | def transform(orig, dest, x, is_kernel): 123 | """ 124 | Transforms an array from the origin dataformat to the destination data format. 125 | Used to convert between the different dataformats used. 126 | 127 | These are (im, ker, im_transpose, ker_transpose): 128 | B = Batch, H = Height, W = Width, C = Channel, I = Input channels, O = Output channels 129 | TF = Tensorflow, PT = Pytorch, Rust = our implementation 130 | 131 | rust: CHW, OIHW, CHW, IOHW 132 | tf: BHWC, HWIO, BHWC, HWOI 133 | pt: BCHW, OIHW, BCHW, IOHW 134 | 135 | orig: str, tf, rust, or pt 136 | dest: str, tf, rust, or pt 137 | x: ndarray to transform 138 | transpose: whether 139 | kernel: bool, True if x is a kernel 140 | """ 141 | orig = orig.lower() 142 | dest = dest.lower() 143 | 144 | if orig == dest: 145 | print("Warning: Tried to convert orig to same dest.") 146 | return x 147 | 148 | # We first convert everything to PT 149 | if orig == "rust": 150 | if not is_kernel: 151 | x = np.expand_dims(x, axis=0) 152 | 153 | elif orig == "tf": 154 | if not is_kernel: 155 | x = np.moveaxis(x, 3, 1) 156 | if is_kernel: 157 | x = np.moveaxis(x, [2, 3], [1, 0]) 158 | 159 | # now x is in PT format 160 | if dest == "rust": 161 | if not is_kernel: 162 | x = np.squeeze(x, axis=0) 163 | # if is_kernel: 164 | # x = np.moveaxis(x, 0, 1) 165 | 166 | elif dest == "tf": 167 | if not is_kernel: 168 | x = np.moveaxis(x, 1, 3) 169 | else: 170 | x = np.moveaxis(x, [0, 1], [3, 2]) 171 | 172 | return x 173 | 174 | 175 | def conv2d_random_array_test(img_shapes, kernel_shapes, num_arrays_per_case=3, transpose=False, seed=260896, padding="VALID", stride=1, bias=False): 176 | """Returns a Test case that can be rendered with the 177 | test_py_impl_random_arrays_template.rs into a Rust test 178 | that tests the conv2d Rust implementation against tf.nn.conv2d. 179 | 180 | num_arrays_per_case: int, number of different random arrays generated 181 | per (img_shape, kernel_shape) combination 182 | """ 183 | # The implementation works as follows with data formats: 184 | # IMG, KER (Rust) --------------- 185 | # \ \ 186 | # \ to pt \ 187 | # v \ 188 | # IMG, KER (PT) \ 189 | # \ \ 190 | # \ pt conv2d \ our implementation 191 | # v \ 192 | # OUT (PT) \ 193 | # \ \ 194 | # \ to rust \ 195 | # v \ 196 | # out1 \ 197 | # | v 198 | # -------------> out2 199 | # Compare (in rust testcase) 200 | 201 | np.random.seed(seed) 202 | 203 | objects = [] 204 | for im_shape, ker_shape in list(itertools.product(img_shapes, kernel_shapes)): 205 | if not transpose and im_shape[0] != ker_shape[1] or transpose and im_shape[0] != ker_shape[0]: 206 | continue # shapes are not compatible, channel size mismatch 207 | 208 | for i in range(num_arrays_per_case): 209 | im = np.random.rand(*im_shape).astype(dtype=np.float32) 210 | ker = np.random.rand(*ker_shape).astype(dtype=np.float32) 211 | if bias: 212 | if transpose: 213 | bias_shape = ker_shape[1] 214 | else: 215 | bias_shape = ker_shape[0] 216 | bias_vec = np.random.rand(bias_shape).astype(dtype=np.float32) 217 | else: 218 | bias_vec = None 219 | 220 | im_pt = torch.FloatTensor(transform_img("rust", "pt", im)) 221 | ker_pt = torch.FloatTensor(transform_ker("rust", "pt", ker)) 222 | if bias: 223 | bias_pt = torch.FloatTensor(bias_vec) 224 | else: 225 | bias_pt = None 226 | 227 | if transpose: 228 | if padding == "SAME": 229 | # This simulates padding = "SAME" 230 | out_pt = torch.nn.functional.conv_transpose2d( 231 | im_pt, ker_pt, bias_pt, output_padding=stride-1, padding=(ker_pt.shape[2] // 2, ker_pt.shape[3]//2), stride=stride) 232 | else: 233 | out_pt = torch.nn.functional.conv_transpose2d( 234 | im_pt, ker_pt, bias_pt, padding=0, stride=stride) 235 | else: 236 | if padding == "SAME" and stride > 1: 237 | out_pt = torch.nn.functional.conv2d( 238 | im_pt, ker_pt, bias_pt, padding=(ker_pt.shape[2] // 2, ker_pt.shape[3]//2), stride=stride) 239 | else: 240 | out_pt = torch.nn.functional.conv2d( 241 | im_pt, ker_pt, bias_pt, padding=padding.lower(), stride=stride) 242 | 243 | out_pt_numpy = transform_img("pt", "rust", out_pt.numpy()) 244 | 245 | out = out_pt_numpy 246 | 247 | test_obj = RandomArrayTestObject( 248 | im, ker, out, bias_vec, padding, stride=stride) 249 | objects.append(test_obj) 250 | 251 | if transpose: 252 | transpose_string = "_transpose" 253 | layer_name = "TransposedConvolutionLayer" 254 | else: 255 | transpose_string = "" 256 | layer_name = "ConvolutionLayer" 257 | 258 | stride_string = f"_stride{stride}" 259 | if bias: 260 | bias_string = "_bias" 261 | else: 262 | bias_string = "" 263 | return RandomArrayTest(f"conv2d{transpose_string}{stride_string}_{padding.lower()}{bias_string}", layer_name, objects) 264 | 265 | 266 | def write_test_to_file(ml_test_folder, test_content, test_name): 267 | test_filename = f"{test_name}_automated_test.rs" 268 | test_output_filepath = os.path.join( 269 | ml_test_folder, test_filename) 270 | with open(test_output_filepath, "w+") as conv2d_output_file: 271 | conv2d_output_file.write(test_content) 272 | print(f"Successfully wrote {test_name} test to {test_output_filepath}") 273 | os.system(f"rustfmt {test_output_filepath}") 274 | print(f"Formatted {test_name} test.") 275 | 276 | 277 | class TestSpecification(): 278 | def __init__(self, image_shapes, kernel_shapes, transpose, padding, bias, stride): 279 | self.image_shapes = image_shapes 280 | self.kernel_shapes = kernel_shapes 281 | self.transpose = transpose 282 | self.padding = padding 283 | self.bias = bias 284 | self.stride = stride 285 | 286 | 287 | def write_out_test(template, ml_test_folder, test_spec): 288 | conv2d_test_case = conv2d_random_array_test( 289 | test_spec.image_shapes, test_spec.kernel_shapes, padding=test_spec.padding, bias=test_spec.bias, stride=test_spec.stride, transpose=test_spec.transpose) 290 | conv2d_test_content = template.render( 291 | random_tests=[conv2d_test_case], file=__file__) 292 | write_test_to_file(ml_test_folder, conv2d_test_content, 293 | conv2d_test_case.test_name) 294 | 295 | 296 | def main(): 297 | # Tensorflow conv2d inputs are given as 298 | # - batch_shape + [in_height, in_width, in_channels] 299 | # and weights as 300 | # - [filter_height * filter_width * in_channels, output_channels] 301 | # See also: https://www.tensorflow.org/api_docs/python/tf/nn/conv2d 302 | # analog for conv2d_transpose: 303 | # https://www.tensorflow.org/api_docs/python/tf/nn/conv2d_transpose 304 | 305 | # Im shapes: Channels, Height, Width. Ker shapes are Out, In, Height, Width 306 | # (the format used in Rust) 307 | 308 | # Note: Tensorflow does not provide an option to input bias; so bias is tested with pytorch 309 | 310 | img_shapes = [(1, 5, 12), (1, 10, 15), (1, 15, 10), 311 | (3, 6, 6), (3, 10, 15), (3, 15, 10)] 312 | kernels_uneven = [(2, 1, 5, 5), (2, 3, 3, 3), (2, 3, 5, 5)] 313 | kernels_even = [(2, 1, 3, 4)] 314 | 315 | img_shapes_trans = [(2, 5, 4), (2, 4, 3), (2, 6, 6), 316 | (1, 4, 5), (1, 3, 3), (3, 2, 2)] 317 | kernels_trans_uneven = [ 318 | (1, 2, 3, 3), (1, 2, 5, 5), (2, 1, 3, 3), (2, 1, 5, 5)] 319 | kernels_trans_even = [ 320 | (3, 2, 4, 4), (3, 2, 6, 6), (2, 1, 4, 4), (1, 1, 4, 4)] 321 | 322 | np.set_printoptions(suppress=True) 323 | # loading Jinja with the random array test template 324 | project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 325 | loader = jinja2.FileSystemLoader( 326 | os.path.join(project_root, "scripts", "templates")) 327 | env = jinja2.Environment(loader=loader) 328 | template = env.get_template("test_py_impl_random_arrays_template.rs") 329 | ml_test_folder = os.path.join(project_root, "tests") 330 | 331 | strides = [1, 2] 332 | paddings = ["VALID", "SAME"] 333 | biases = [True, False] 334 | transposes = [True, False] 335 | 336 | for stride, padding, bias, transpose in itertools.product(strides, paddings, biases, transposes): 337 | ishapes = img_shapes_trans if transpose else img_shapes 338 | # handling some special cases with same padding, even kernels are problematic there 339 | if transpose and padding.lower() == "same": 340 | kshapes = kernels_trans_uneven 341 | elif not transpose and padding.lower() == "same": 342 | kshapes = kernels_uneven 343 | elif transpose: 344 | kshapes = kernels_trans_uneven + kernels_trans_even 345 | else: 346 | kshapes = kernels_uneven + kernels_even 347 | spec = TestSpecification( 348 | ishapes, kshapes, transpose, padding, bias, stride) 349 | write_out_test(template, ml_test_folder, spec) 350 | 351 | 352 | if __name__ == "__main__": 353 | main() 354 | -------------------------------------------------------------------------------- /scripts/templates/test_py_impl_random_arrays_template.rs: -------------------------------------------------------------------------------- 1 | {# 2 | Template file for generating automated unit tests. 3 | Use the generate_tests.py script to regenerate. 4 | #} 5 | // This file has been automatically generated by Jinja2 via the 6 | // script {{ file }}. 7 | // Please do not change this file by hand. 8 | #[allow(unused_imports)] 9 | use convolutions_rs::convolutions::*; 10 | #[allow(unused_imports)] 11 | use convolutions_rs::transposed_convolutions::*; 12 | #[allow(unused_imports)] 13 | use convolutions_rs::Padding; 14 | #[allow(unused_imports)] 15 | use ndarray::{Array, array, Dimension, Array3, Array4, Array2}; 16 | 17 | fn arr_allclose(current: &Array, target: &Array) -> bool { 18 | assert_eq!(current.shape(), target.shape(), "\ngiven array had shape {:?}, but target had shape {:?}", current.shape(), target.shape()); 19 | (current - target).map(|x| (*x as f32).abs()).sum() < 1e-3 20 | } 21 | 22 | {% for t in random_tests %} 23 | #[test] 24 | fn test_py_implementation_random_arrays_{{t.test_name}}() { 25 | {% for r in t.random_test_objects %} 26 | let test_input{{loop.index}} = {{ r.input_arr }}; 27 | {# The type hint is needed for the array-makro #} 28 | let kernel{{loop.index}}: Array4 = {{ r.kernel }}; 29 | let conv_layer{{loop.index}} = {{t.layer_name}}::new(kernel{{loop.index}}, {{r.bias}}, {{r.stride}}, {{r.padding}}); 30 | let target_output{{loop.index}}: Array3 = {{ r.output_arr }}; 31 | let current_output{{loop.index}}: Array3 = conv_layer{{loop.index}}.{{t.function_name}}(&test_input{{loop.index}}); 32 | 33 | assert!(arr_allclose(¤t_output{{loop.index}}, &target_output{{loop.index}}), 34 | "{:?} was not equal to {:?}", current_output{{loop.index}}, target_output{{loop.index}}); 35 | {% endfor %} 36 | } 37 | {% endfor %} 38 | -------------------------------------------------------------------------------- /src/convolutions.rs: -------------------------------------------------------------------------------- 1 | //! Module that contains classical convolutions, as used f.e. in convolutional neural networks. 2 | //! 3 | //! More can be read here: 4 | //! - 5 | 6 | use crate::{DataRepresentation, Padding}; 7 | use ndarray::*; 8 | use num_traits::Float; 9 | 10 | /// Rust implementation of a convolutional layer. 11 | /// The weight matrix shall have dimension (in that order) 12 | /// (input channels, output channels, kernel width, kernel height), 13 | /// to comply with the order in which pytorch weights are saved. 14 | pub struct ConvolutionLayer { 15 | /// Weight matrix of the kernel 16 | pub(in crate) kernel: Array4, 17 | pub(in crate) bias: Option>, 18 | pub(in crate) stride: usize, 19 | pub(in crate) padding: Padding, 20 | } 21 | 22 | impl ConvolutionLayer { 23 | /// Creates new convolution layer. 24 | /// The weights are given in Pytorch layout. 25 | /// (out channels, in channels, kernel height, kernel width) 26 | /// Bias: (output height * output width, 1) 27 | pub fn new( 28 | weights: Array4, 29 | bias_array: Option>, 30 | stride: usize, 31 | padding: Padding, 32 | ) -> ConvolutionLayer { 33 | assert!(stride > 0, "Stride of 0 passed"); 34 | ConvolutionLayer { 35 | kernel: weights, 36 | bias: bias_array, 37 | stride, 38 | padding, 39 | } 40 | } 41 | 42 | /// Creates new convolution layer. The weights are given in 43 | /// Tensorflow layout. 44 | /// (kernel height, kernel width, in channels, out channels) 45 | pub fn new_tf( 46 | weights: Array4, 47 | bias_array: Option>, 48 | stride: usize, 49 | padding: Padding, 50 | ) -> ConvolutionLayer { 51 | let permuted_view = weights.view().permuted_axes([3, 2, 0, 1]); 52 | // Hack to fix the memory layout, permuted axes makes a 53 | // col major array / non-contiguous array from weights 54 | let permuted_array: Array4 = 55 | Array::from_shape_vec(permuted_view.dim(), permuted_view.iter().copied().collect()) 56 | .unwrap(); 57 | ConvolutionLayer::new(permuted_array, bias_array, stride, padding) 58 | } 59 | 60 | /// Analog to conv2d. 61 | pub fn convolve(&self, image: &DataRepresentation) -> DataRepresentation { 62 | conv2d( 63 | &self.kernel, 64 | self.bias.as_ref(), 65 | image, 66 | self.padding, 67 | self.stride, 68 | ) 69 | } 70 | } 71 | 72 | pub(in crate) fn get_padding_size( 73 | input_h: usize, 74 | input_w: usize, 75 | stride: usize, 76 | kernel_h: usize, 77 | kernel_w: usize, 78 | ) -> (usize, usize, usize, usize, usize, usize) { 79 | let pad_along_height: usize; 80 | let pad_along_width: usize; 81 | let idx_0: usize = 0; 82 | 83 | if input_h % stride == idx_0 { 84 | pad_along_height = (kernel_h - stride).max(idx_0); 85 | } else { 86 | pad_along_height = (kernel_h - (input_h % stride)).max(idx_0); 87 | }; 88 | if input_w % stride == idx_0 { 89 | pad_along_width = (kernel_w - stride).max(idx_0); 90 | } else { 91 | pad_along_width = (kernel_w - (input_w % stride)).max(idx_0); 92 | }; 93 | 94 | let pad_top = pad_along_height / 2; 95 | let pad_bottom = pad_along_height - pad_top; 96 | let pad_left = pad_along_width / 2; 97 | let pad_right = pad_along_width - pad_left; 98 | 99 | // yes top/bottom and right/left are swapped. No, I don't know 100 | // why this change makes it conform to the pytorchn implementation. 101 | ( 102 | pad_along_height, 103 | pad_along_width, 104 | pad_bottom, 105 | pad_top, 106 | pad_right, 107 | pad_left, 108 | ) 109 | } 110 | 111 | pub(in crate) fn im2col_ref<'a, T, F: 'a + Float>( 112 | im_arr: T, 113 | ker_height: usize, 114 | ker_width: usize, 115 | im_height: usize, 116 | im_width: usize, 117 | im_channel: usize, 118 | stride: usize, 119 | ) -> Array2 120 | where 121 | // Args: 122 | // im_arr: image matrix to be translated into columns, (C,H,W) 123 | // ker_height: filter height (hh) 124 | // ker_width: filter width (ww) 125 | // im_height: image height 126 | // im_width: image width 127 | // 128 | // Returns: 129 | // col: (new_h*new_w,hh*ww*C) matrix, each column is a cube that will convolve with a filter 130 | // new_h = (H-hh) // stride + 1, new_w = (W-ww) // stride + 1 131 | T: AsArray<'a, F, Ix3>, 132 | { 133 | let im2d_arr: ArrayView3 = im_arr.into(); 134 | let new_h = (im_height - ker_height) / stride + 1; 135 | let new_w = (im_width - ker_width) / stride + 1; 136 | let mut cols_img: Array2 = 137 | Array::zeros((new_h * new_w, im_channel * ker_height * ker_width)); 138 | let mut cont = 0_usize; 139 | for i in 1..new_h + 1 { 140 | for j in 1..new_w + 1 { 141 | let patch = im2d_arr.slice(s![ 142 | .., 143 | (i - 1) * stride..((i - 1) * stride + ker_height), 144 | (j - 1) * stride..((j - 1) * stride + ker_width), 145 | ]); 146 | let patchrow_unwrap: Array1 = Array::from_iter(patch.map(|a| *a)); 147 | 148 | cols_img.row_mut(cont).assign(&patchrow_unwrap); 149 | cont += 1; 150 | } 151 | } 152 | cols_img 153 | } 154 | 155 | /// Performs a convolution on the given image data using this layers parameters. 156 | /// We always convolve on flattened images and expect the input array in im2col 157 | /// style format. 158 | /// 159 | /// Read more here: 160 | /// - 161 | /// 162 | /// Input: 163 | /// ----------------------------------------------- 164 | /// - kernel_weights: weights of shape (F, C, HH, WW) 165 | /// - im2d: Input data of shape (C, H, W) 166 | /// ----------------------------------------------- 167 | /// - 'stride': The number of pixels between adjacent receptive fields in the 168 | /// horizontal and vertical directions, must be int 169 | /// - 'pad': "Same" or "Valid" 170 | 171 | /// Returns: 172 | /// ----------------------------------------------- 173 | /// - out: Output data, of shape (F, H', W') 174 | pub fn conv2d<'a, T, V, F: 'static + Float + std::ops::AddAssign>( 175 | kernel_weights: T, 176 | bias: Option<&Array1>, 177 | im2d: V, 178 | padding: Padding, 179 | stride: usize, 180 | ) -> DataRepresentation 181 | where 182 | // This trait bound ensures that kernel and im2d can be passed as owned array or view. 183 | // AsArray just ensures that im2d can be converted to an array view via ".into()". 184 | // Read more here: https://docs.rs/ndarray/0.12.1/ndarray/trait.AsArray.html 185 | V: AsArray<'a, F, Ix3>, 186 | T: AsArray<'a, F, Ix4>, 187 | { 188 | // Initialisations 189 | let im2d_arr: ArrayView3 = im2d.into(); 190 | let kernel_weights_arr: ArrayView4 = kernel_weights.into(); 191 | let im_col: Array2; // output of fn: im2col_ref() 192 | let new_im_height: usize; 193 | let new_im_width: usize; 194 | let weight_shape = kernel_weights_arr.shape(); 195 | let num_filters = weight_shape[0] as usize; 196 | let num_channels_out = weight_shape[1] as usize; 197 | let kernel_height = weight_shape[2] as usize; 198 | let kernel_width = weight_shape[3] as usize; 199 | 200 | // Dimensions: C, H, W 201 | let im_channel = im2d_arr.len_of(Axis(0)); 202 | let im_height = im2d_arr.len_of(Axis(1)); 203 | let im_width = im2d_arr.len_of(Axis(2)); 204 | 205 | // Calculate output shapes H', W' for two types of Padding 206 | if padding == Padding::Same { 207 | // https://mmuratarat.github.io/2019-01-17/implementing-padding-schemes-of-tensorflow-in-python 208 | // H' = H / stride 209 | // W' = W / stride 210 | 211 | let h_float = im_height as f32; 212 | let w_float = im_width as f32; 213 | let stride_float = stride as f32; 214 | 215 | let new_im_height_float = (h_float / stride_float).ceil(); 216 | let new_im_width_float = (w_float / stride_float).ceil(); 217 | 218 | new_im_height = new_im_height_float as usize; 219 | new_im_width = new_im_width_float as usize; 220 | } else { 221 | // H' = ((H - HH) / stride ) + 1 222 | // W' = ((W - WW) / stride ) + 1 223 | new_im_height = ((im_height - kernel_height) / stride) + 1; 224 | new_im_width = ((im_width - kernel_width) / stride) + 1; 225 | }; 226 | 227 | // weights.reshape(F, HH*WW*C) 228 | let filter_col = kernel_weights_arr 229 | .into_shape((num_filters, kernel_height * kernel_width * num_channels_out)) 230 | .unwrap(); 231 | 232 | // fn:im2col() for different Paddings 233 | if padding == Padding::Same { 234 | // https://mmuratarat.github.io/2019-01-17/implementing-padding-schemes-of-tensorflow-in-python 235 | let (pad_num_h, pad_num_w, pad_top, pad_bottom, pad_left, pad_right) = 236 | get_padding_size(im_height, im_width, stride, kernel_height, kernel_width); 237 | let mut im2d_arr_pad: Array3 = Array::zeros(( 238 | num_channels_out, 239 | im_height + pad_num_h, 240 | im_width + pad_num_w, 241 | )); 242 | let pad_bottom_int = (im_height + pad_num_h) - pad_bottom; 243 | let pad_right_int = (im_width + pad_num_w) - pad_right; 244 | // https://github.com/rust-ndarray/ndarray/issues/823 245 | im2d_arr_pad 246 | .slice_mut(s![.., pad_top..pad_bottom_int, pad_left..pad_right_int]) 247 | .assign(&im2d_arr); 248 | 249 | let im_height_pad = im2d_arr_pad.len_of(Axis(1)); 250 | let im_width_pad = im2d_arr_pad.len_of(Axis(2)); 251 | 252 | im_col = im2col_ref( 253 | im2d_arr_pad.view(), 254 | kernel_height, 255 | kernel_width, 256 | im_height_pad, 257 | im_width_pad, 258 | im_channel, 259 | stride, 260 | ); 261 | } else { 262 | im_col = im2col_ref( 263 | im2d_arr, 264 | kernel_height, 265 | kernel_width, 266 | im_height, 267 | im_width, 268 | im_channel, 269 | stride, 270 | ); 271 | }; 272 | let filter_transpose = filter_col.t(); 273 | let mul = im_col.dot(&filter_transpose); 274 | let output = mul 275 | .into_shape((new_im_height, new_im_width, num_filters)) 276 | .unwrap() 277 | .permuted_axes([2, 0, 1]); 278 | 279 | add_bias(&output, bias) 280 | } 281 | 282 | pub(in crate) fn add_bias(x: &Array3, bias: Option<&Array1>) -> Array3 283 | where 284 | F: 'static + Float + std::ops::AddAssign, 285 | { 286 | if let Some(bias_array) = bias { 287 | assert!( 288 | bias_array.shape()[0] == x.shape()[0], 289 | "Bias array has the wrong shape {:?} for vec of shape {:?}", 290 | bias_array.shape(), 291 | x.shape() 292 | ); 293 | // Yes this is really necessary. Broadcasting with ndarray-rust 294 | // starts at the right side of the shape, so we have to add 295 | // the axes by hand (else it thinks that it should compare the 296 | // output width and the bias channels). 297 | (x + &bias_array 298 | .clone() 299 | .insert_axis(Axis(1)) 300 | .insert_axis(Axis(2)) 301 | .broadcast(x.shape()) 302 | .unwrap()) 303 | .into_dimensionality() 304 | .unwrap() 305 | } else { 306 | x.clone() 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This package provides normal convolutions as well as transposed convolutions. 2 | //! We provide both in the form of free functions as well as something resembling a neural network layer. 3 | //! This crate also requires ndarray to use the functions, as input and output are in the form of ndarrays. 4 | //! 5 | //! In all implementations, we conform to the Pytorch implementation of convolutions (which agrees with the Tensorflow implementation, up to shapes). 6 | //! We have used the technique described in this blog to achieve a fast implementation: 7 | //! - 8 | //! 9 | //! Example: 10 | //! ``` 11 | //! use convolutions_rs::convolutions::*; 12 | //! use ndarray::*; 13 | //! use convolutions_rs::Padding; 14 | //! 15 | //! // Input has shape (channels, height, width) 16 | //! let input = Array::from_shape_vec( 17 | //! (1, 4, 4), 18 | //! vec![1.,2.,3.,4.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.] 19 | //! ) 20 | //! .unwrap(); 21 | //! 22 | //! // Kernel has shape (channels out, channels in, height, width) 23 | //! let kernel: Array4 = Array::from_shape_vec( 24 | //! (2,1,2,2), 25 | //! vec![1.,1.,1.,1.,1.,1.,1.,1.] 26 | //! ) 27 | //! .unwrap(); 28 | //! 29 | //! let conv_layer = ConvolutionLayer::new(kernel.clone(), None, 1, Padding::Valid); 30 | //! let output_layer: Array3 = conv_layer.convolve(&input); 31 | //! let output_free = conv2d(&kernel, None, &input, Padding::Valid, 1); 32 | //! 33 | //! println!("Layer: {:?}", output_layer); 34 | //! println!("Free: {:?}", output_free); 35 | //! ``` 36 | 37 | use ndarray::{Array3, Array4}; 38 | 39 | pub mod convolutions; 40 | pub mod transposed_convolutions; 41 | 42 | pub type DataRepresentation = Array3; 43 | pub type ConvKernel = Array4; 44 | 45 | /// Padding (specific way of adding zeros to the input matrix) kind used in the convolution. 46 | #[derive(PartialEq, Debug, Clone, Copy)] 47 | pub enum Padding { 48 | /// Output has the same shape as input. 49 | Same, 50 | /// Padding is only used to make input fit the kernel. 51 | Valid, 52 | } 53 | -------------------------------------------------------------------------------- /src/transposed_convolutions.rs: -------------------------------------------------------------------------------- 1 | //! Module that contains transposed convolutions (also called deconvolution layers). 2 | //! 3 | //! More can be read here: 4 | //! - 5 | //! - 6 | //! - 7 | use crate::{ 8 | convolutions::{add_bias, ConvolutionLayer}, 9 | ConvKernel, DataRepresentation, Padding, 10 | }; 11 | use ndarray::*; 12 | use num_traits::Float; 13 | use std::ops::AddAssign; 14 | 15 | /// Analog to a Convolution Layer 16 | pub struct TransposedConvolutionLayer { 17 | convolution_layer: ConvolutionLayer, 18 | } 19 | 20 | impl TransposedConvolutionLayer { 21 | /// Creates new transposed_convolutionLayer. The weights are given in 22 | /// Pytorch layout. 23 | /// (in channels, out channels, kernel_height, kernel_width) 24 | pub fn new( 25 | weights: ConvKernel, 26 | bias: Option>, 27 | stride: usize, 28 | padding: Padding, 29 | ) -> TransposedConvolutionLayer { 30 | TransposedConvolutionLayer { 31 | convolution_layer: ConvolutionLayer::new(weights, bias, stride, padding), 32 | } 33 | } 34 | 35 | /// Creates new transposed_convolutionLayer. The weights are given in 36 | /// Tensorflow layout. 37 | /// (kernel height, kernel width, out channels, in channels) 38 | pub fn new_tf( 39 | weights: ConvKernel, 40 | bias: Option>, 41 | stride: usize, 42 | padding: Padding, 43 | ) -> TransposedConvolutionLayer { 44 | TransposedConvolutionLayer { 45 | convolution_layer: ConvolutionLayer::new_tf(weights, bias, stride, padding), 46 | } 47 | } 48 | 49 | /// Analog to conv_transpose2d. 50 | pub fn transposed_convolve(&self, image: &DataRepresentation) -> DataRepresentation { 51 | let output = conv_transpose2d( 52 | &self.convolution_layer.kernel, 53 | self.convolution_layer.bias.as_ref(), 54 | &image.view(), 55 | self.convolution_layer.padding, 56 | self.convolution_layer.stride, 57 | ); 58 | output 59 | } 60 | } 61 | 62 | /// Implementation of col2im as seen in the Pytorch cpp implementation. 63 | /// This implementation doesn't just assign the matrix patch-wise to a new 64 | /// matrix, but also sums the patches such that it unrolls the result 65 | /// of conv-transpose. 66 | /// 67 | /// mat is a matrix of size (image_width * image_height, c_out * k_h * k_w), 68 | /// in which each entry is the value of a kernel pixel multiplied with an image pixels 69 | /// with the channels summed out 70 | /// 71 | /// The naming is a bit confusing, as this function is originally used in the backward 72 | /// pass. What this function returns is simply an array of (channels, height, width). 73 | /// 74 | fn col2im_pt<'a, F: 'a + Float + AddAssign>( 75 | data_col: &[F], 76 | channels: usize, 77 | height: usize, 78 | width: usize, 79 | output_height: usize, 80 | output_width: usize, 81 | kernel_h: usize, 82 | kernel_w: usize, 83 | pad_h: usize, 84 | pad_w: usize, 85 | stride: usize, 86 | ) -> DataRepresentation { 87 | let mut res: Array1 = Array::zeros(channels * height * width); 88 | 89 | let height_col = output_height; 90 | let width_col = output_width; 91 | let channels_col = channels * kernel_h * kernel_w; 92 | 93 | for c_col in 0..channels_col { 94 | let w_offset = c_col % kernel_w; 95 | let h_offset = (c_col / kernel_w) % kernel_h; 96 | let c_im = c_col / kernel_h / kernel_w; 97 | 98 | for h_col in 0..height_col { 99 | let h_im = (h_col * stride) as i64 - pad_h as i64 + h_offset as i64; 100 | 101 | for w_col in 0..width_col { 102 | let w_im = (w_col * stride) as i64 - pad_w as i64 + w_offset as i64; 103 | 104 | if h_im < height as i64 && h_im >= 0 && w_im < width as i64 && w_im >= 0 { 105 | unsafe { 106 | let el = 107 | res.uget_mut((c_im * height + h_im as usize) * width + w_im as usize); 108 | el.add_assign( 109 | *data_col 110 | .get_unchecked((c_col * height_col + h_col) * width_col + w_col), 111 | ); 112 | } 113 | } 114 | } 115 | } 116 | } 117 | Array::from_shape_vec((channels, height, width), res.into_iter().collect()).unwrap() 118 | } 119 | 120 | /// Performs a transposed convolution on the input image. Might be used to upsample the image. 121 | /// This implementation is identical to the supplied by Pytorch and works by performing 122 | /// a "backwards pass" on the input. 123 | /// 124 | /// More explanation can be read here: 125 | /// - 126 | /// 127 | /// The implementation is mostly taken from Pytorch. 128 | /// 129 | /// NOTE: THERE IS A CHANGE IN KERNEL DIMENSIONS FOR CONV TRANSPOSED 130 | /// Input: 131 | /// ----------------------------------------------- 132 | /// - im2d: Input data of shape (C, H, W) 133 | /// - kernel_weights: Filter weights of shape (C, F, HH, WW) // DIFFERENT from CONV2D 134 | /// ----------------------------------------------- 135 | /// - 'stride': The number of pixels between adjacent receptive fields in the 136 | /// horizontal and vertical directions, must be int 137 | /// - 'pad': "Same" or "Valid" 138 | 139 | /// Returns: 140 | /// ----------------------------------------------- 141 | /// - out: Output data, of shape (F, H', W') 142 | pub fn conv_transpose2d<'a, T, V, F: 'static + Float + AddAssign>( 143 | kernel_weights: T, 144 | bias: Option<&Array1>, 145 | im2d: V, 146 | padding: Padding, 147 | stride: usize, 148 | ) -> DataRepresentation 149 | where 150 | V: AsArray<'a, F, Ix3>, 151 | T: AsArray<'a, F, Ix4>, 152 | { 153 | let kernel_mat: ArrayView4 = kernel_weights.into(); 154 | let im_mat: ArrayView3 = im2d.into(); 155 | // kernel has shape (C, F, HH, WW) 156 | let k_c = kernel_mat.shape()[0]; 157 | let k_f = kernel_mat.shape()[1]; 158 | let k_h = kernel_mat.shape()[2]; 159 | let k_w = kernel_mat.shape()[3]; 160 | let ker_reshape = kernel_mat.into_shape((k_c, k_f * k_h * k_w)).unwrap(); 161 | let im_c = im_mat.shape()[0]; 162 | let im_h = im_mat.shape()[1]; 163 | let im_w = im_mat.shape()[2]; 164 | let im_mat_contig = im_mat.as_standard_layout(); 165 | let im_reshape = im_mat_contig.into_shape((im_c, im_h * im_w)).unwrap(); 166 | let data_matrix: Array2 = ker_reshape.t().dot(&im_reshape); 167 | // output padding might be necessary to achieve the desired shape 168 | let (pad_h, pad_w, output_pad_h, output_pad_w) = match padding { 169 | Padding::Valid => (0, 0, 0, 0), 170 | Padding::Same => (k_h / 2, k_w / 2, stride - 1, stride - 1), 171 | }; 172 | let output_height = (im_h - 1) * stride + k_h + output_pad_h - 2 * pad_h; 173 | let output_width = (im_w - 1) * stride + k_w + output_pad_w - 2 * pad_w; 174 | let t_conv_output = col2im_pt( 175 | data_matrix.as_slice().unwrap(), 176 | k_f, 177 | output_height, 178 | output_width, 179 | im_h, 180 | im_w, 181 | k_h, 182 | k_w, 183 | pad_h, 184 | pad_w, 185 | stride, 186 | ); 187 | return add_bias(&t_conv_output, bias); 188 | } 189 | -------------------------------------------------------------------------------- /tests/conv2d_transpose_stride1_same_automated_test.rs: -------------------------------------------------------------------------------- 1 | // This file has been automatically generated by Jinja2 via the 2 | // script /Users/almico/projects/convolutions-rs/scripts/generate_tests.py. 3 | // Please do not change this file by hand. 4 | #[allow(unused_imports)] 5 | use convolutions_rs::convolutions::*; 6 | #[allow(unused_imports)] 7 | use convolutions_rs::transposed_convolutions::*; 8 | #[allow(unused_imports)] 9 | use convolutions_rs::Padding; 10 | #[allow(unused_imports)] 11 | use ndarray::{array, Array, Array2, Array3, Array4, Dimension}; 12 | 13 | fn arr_allclose(current: &Array, target: &Array) -> bool { 14 | assert_eq!( 15 | current.shape(), 16 | target.shape(), 17 | "\ngiven array had shape {:?}, but target had shape {:?}", 18 | current.shape(), 19 | target.shape() 20 | ); 21 | (current - target).map(|x| (*x as f32).abs()).sum() < 1e-3 22 | } 23 | 24 | #[test] 25 | fn test_py_implementation_random_arrays_conv2d_transpose_stride1_same() { 26 | let test_input1 = Array::from_shape_vec( 27 | (2, 5, 4), 28 | vec![ 29 | 0.17014849, 0.4305688, 0.5715329, 0.06520256, 0.12669589, 0.7501565, 0.9837982, 30 | 0.55574155, 0.04181346, 0.23677547, 0.51154923, 0.02844254, 0.60484785, 0.72306335, 31 | 0.22177844, 0.16487044, 0.46672952, 0.54035133, 0.6922357, 0.27845532, 0.66966337, 32 | 0.41083884, 0.4583148, 0.70402896, 0.6177326, 0.9269775, 0.56033564, 0.9098013, 33 | 0.2697065, 0.24242379, 0.7944849, 0.75231165, 0.9692583, 0.12854727, 0.9148518, 34 | 0.3356524, 0.37189406, 0.55898565, 0.5888119, 0.44166553, 35 | ], 36 | ) 37 | .unwrap(); 38 | 39 | let kernel1: Array4 = Array::from_shape_vec( 40 | (2, 1, 3, 3), 41 | vec![ 42 | 0.9034325, 0.2795916, 0.7567664, 0.85028297, 0.96145767, 0.5566679, 0.84558666, 43 | 0.0474241, 0.23985276, 0.07658575, 0.7197864, 0.13313323, 0.69580543, 0.12692, 44 | 0.38484824, 0.775336, 0.52113837, 0.4364637, 45 | ], 46 | ) 47 | .unwrap(); 48 | let conv_layer1 = TransposedConvolutionLayer::new(kernel1, None, 1, Padding::Same); 49 | let target_output1: Array3 = Array::from_shape_vec( 50 | (1, 5, 4), 51 | vec![ 52 | 2.129321, 3.6102068, 3.4920175, 2.2759285, 2.961033, 4.6110344, 5.056102, 3.1625488, 53 | 3.655266, 4.6101437, 4.923399, 2.2716956, 2.868677, 5.1868505, 3.7493901, 2.5375302, 54 | 2.5892203, 3.5579548, 2.9167597, 1.5709618, 55 | ], 56 | ) 57 | .unwrap(); 58 | let current_output1: Array3 = conv_layer1.transposed_convolve(&test_input1); 59 | 60 | assert!( 61 | arr_allclose(¤t_output1, &target_output1), 62 | "{:?} was not equal to {:?}", 63 | current_output1, 64 | target_output1 65 | ); 66 | 67 | let test_input2 = Array::from_shape_vec( 68 | (2, 5, 4), 69 | vec![ 70 | 0.14352316, 0.8997107, 0.64410555, 0.04471071, 0.767672, 0.43464628, 0.16569944, 71 | 0.18875164, 0.12285258, 0.2781115, 0.5390728, 0.5066572, 0.97435564, 0.39133722, 72 | 0.7964828, 0.988919, 0.35985747, 0.00756764, 0.53660643, 0.8659267, 0.8576183, 73 | 0.81628793, 0.9480399, 0.45711017, 0.89837223, 0.8462714, 0.70536447, 0.9289133, 74 | 0.0067116, 0.65220493, 0.72789615, 0.00785976, 0.32536873, 0.09833383, 0.1022715, 75 | 0.7567798, 0.23972042, 0.38848338, 0.00744711, 0.8715701, 76 | ], 77 | ) 78 | .unwrap(); 79 | 80 | let kernel2: Array4 = Array::from_shape_vec( 81 | (2, 1, 3, 3), 82 | vec![ 83 | 0.07988323, 0.6283005, 0.8241853, 0.16570753, 0.6487234, 0.14438818, 0.6286194, 84 | 0.34163022, 0.03235205, 0.25922647, 0.59344524, 0.9308157, 0.22162326, 0.91818297, 85 | 0.97700953, 0.18019113, 0.775954, 0.14120784, 86 | ], 87 | ) 88 | .unwrap(); 89 | let conv_layer2 = TransposedConvolutionLayer::new(kernel2, None, 1, Padding::Same); 90 | let target_output2: Array3 = Array::from_shape_vec( 91 | (1, 5, 4), 92 | vec![ 93 | 2.4801147, 4.948943, 4.248997, 2.9309442, 3.2820787, 4.7746215, 4.7418694, 3.657943, 94 | 2.5235906, 3.742593, 4.055015, 3.8372264, 1.826671, 2.8332467, 3.2692723, 3.3612194, 95 | 1.3899677, 1.5449216, 2.2082677, 2.4120436, 96 | ], 97 | ) 98 | .unwrap(); 99 | let current_output2: Array3 = conv_layer2.transposed_convolve(&test_input2); 100 | 101 | assert!( 102 | arr_allclose(¤t_output2, &target_output2), 103 | "{:?} was not equal to {:?}", 104 | current_output2, 105 | target_output2 106 | ); 107 | 108 | let test_input3 = Array::from_shape_vec( 109 | (2, 5, 4), 110 | vec![ 111 | 0.3426181, 0.5417864, 0.7768226, 0.34603763, 0.6114103, 0.9716041, 0.5157695, 112 | 0.50755495, 0.6659802, 0.629322, 0.60627973, 0.27978492, 0.28792506, 0.7547703, 113 | 0.0509604, 0.10449678, 0.89887625, 0.6572328, 0.695583, 0.3626411, 0.37613922, 114 | 0.9241278, 0.39898983, 0.6908677, 0.5511301, 0.36253917, 0.36786652, 0.88718724, 115 | 0.69587743, 0.4870034, 0.7135373, 0.9862549, 0.22876498, 0.75677496, 0.5617529, 116 | 0.5566727, 0.7035832, 0.92333794, 0.85647196, 0.36252776, 117 | ], 118 | ) 119 | .unwrap(); 120 | 121 | let kernel3: Array4 = Array::from_shape_vec( 122 | (2, 1, 3, 3), 123 | vec![ 124 | 0.9373231, 0.01684272, 0.34317794, 0.921993, 0.36392415, 0.7462054, 0.7556754, 125 | 0.31284246, 0.4031665, 0.7376267, 0.7926341, 0.36348057, 0.84374106, 0.03665259, 126 | 0.23846498, 0.82509995, 0.97324103, 0.96826524, 127 | ], 128 | ) 129 | .unwrap(); 130 | let conv_layer3 = TransposedConvolutionLayer::new(kernel3, None, 1, Padding::Same); 131 | let target_output3: Array3 = Array::from_shape_vec( 132 | (1, 5, 4), 133 | vec![ 134 | 3.0429893, 3.0979333, 3.7195776, 1.8485445, 4.601478, 6.200628, 6.763774, 3.4238458, 135 | 4.4718704, 5.45767, 5.7891073, 3.01157, 5.0805173, 6.34371, 5.842485, 3.0566144, 136 | 3.245428, 4.2877626, 3.7733746, 2.0074868, 137 | ], 138 | ) 139 | .unwrap(); 140 | let current_output3: Array3 = conv_layer3.transposed_convolve(&test_input3); 141 | 142 | assert!( 143 | arr_allclose(¤t_output3, &target_output3), 144 | "{:?} was not equal to {:?}", 145 | current_output3, 146 | target_output3 147 | ); 148 | 149 | let test_input4 = Array::from_shape_vec( 150 | (2, 5, 4), 151 | vec![ 152 | 0.77620417, 0.8427075, 0.42989048, 0.76771307, 0.54514444, 0.7878393, 0.21897991, 153 | 0.975659, 0.73295325, 0.69994044, 0.86701024, 0.76087946, 0.623545, 0.14890751, 154 | 0.8610666, 0.21943341, 0.1644093, 0.6089244, 0.8612485, 0.02602104, 0.30423534, 155 | 0.5087405, 0.16869895, 0.3329467, 0.179494, 0.33126613, 0.11996287, 0.21606164, 156 | 0.87119263, 0.33579683, 0.2313126, 0.2576527, 0.3748985, 0.30113342, 0.8142977, 157 | 0.423247, 0.8179498, 0.92175007, 0.21606776, 0.19149332, 158 | ], 159 | ) 160 | .unwrap(); 161 | 162 | let kernel4: Array4 = Array::from_shape_vec( 163 | (2, 1, 5, 5), 164 | vec![ 165 | 0.4240341, 0.14780204, 0.46443272, 0.00207204, 0.359202, 0.9019851, 0.21363449, 166 | 0.6390296, 0.5977058, 0.81350476, 0.3685356, 0.90738845, 0.7828945, 0.9812083, 167 | 0.6726924, 0.46724817, 0.17302093, 0.39839587, 0.78055173, 0.8576361, 0.7335981, 168 | 0.52934057, 0.9529279, 0.78965247, 0.46750203, 0.08403921, 0.73126566, 0.34934825, 169 | 0.3459232, 0.54770446, 0.68714255, 0.29283327, 0.53392637, 0.8850151, 0.6909357, 170 | 0.44388366, 0.9043074, 0.9949724, 0.64743847, 0.36691284, 0.5396585, 0.5372604, 171 | 0.47547424, 0.44373918, 0.34716287, 0.21774843, 0.9437953, 0.51856595, 0.15765554, 172 | 0.9350713, 173 | ], 174 | ) 175 | .unwrap(); 176 | let conv_layer4 = TransposedConvolutionLayer::new(kernel4, None, 1, Padding::Same); 177 | let target_output4: Array3 = Array::from_shape_vec( 178 | (1, 5, 4), 179 | vec![ 180 | 4.7387366, 6.7601576, 6.741786, 5.0231147, 6.1374135, 8.490736, 9.399002, 6.5965943, 181 | 9.325594, 11.029676, 11.6051235, 8.902636, 6.881201, 9.702231, 10.010415, 7.9663415, 182 | 6.7219944, 7.777974, 7.9563823, 5.7190313, 183 | ], 184 | ) 185 | .unwrap(); 186 | let current_output4: Array3 = conv_layer4.transposed_convolve(&test_input4); 187 | 188 | assert!( 189 | arr_allclose(¤t_output4, &target_output4), 190 | "{:?} was not equal to {:?}", 191 | current_output4, 192 | target_output4 193 | ); 194 | 195 | let test_input5 = Array::from_shape_vec( 196 | (2, 5, 4), 197 | vec![ 198 | 0.86233217, 0.81734437, 0.6566154, 0.3879487, 0.28848994, 0.5814131, 0.06870039, 199 | 0.78543746, 0.47194287, 0.12733267, 0.6111727, 0.25675833, 0.74741316, 0.45714313, 200 | 0.9649919, 0.57969916, 0.5208711, 0.93666834, 0.13247305, 0.7087714, 0.17877895, 201 | 0.40767, 0.6906785, 0.9675369, 0.224454, 0.28950995, 0.75431514, 0.99582964, 0.8120864, 202 | 0.9536324, 0.7703749, 0.683158, 0.16371861, 0.42877796, 0.7300311, 0.9456737, 203 | 0.05700503, 0.19623296, 0.44457257, 0.5512328, 204 | ], 205 | ) 206 | .unwrap(); 207 | 208 | let kernel5: Array4 = Array::from_shape_vec( 209 | (2, 1, 5, 5), 210 | vec![ 211 | 0.15101007, 0.25140873, 0.2031063, 0.5537751, 0.3641559, 0.701937, 0.27669305, 212 | 0.85076064, 0.00186597, 0.3316532, 0.15485734, 0.35000807, 0.42372492, 0.03995521, 213 | 0.5067197, 0.38907993, 0.37287286, 0.21294773, 0.8646356, 0.10154326, 0.75313085, 214 | 0.7910046, 0.15368015, 0.25977603, 0.40315583, 0.635939, 0.25689796, 0.13678746, 215 | 0.43744904, 0.6658951, 0.564682, 0.70429957, 0.67241764, 0.15790685, 0.25059524, 216 | 0.6432024, 0.60438925, 0.45131883, 0.96080875, 0.40131757, 0.60942906, 0.7035094, 217 | 0.6473561, 0.05433872, 0.7478619, 0.24740443, 0.35665986, 0.00020906, 0.23741227, 218 | 0.57013196, 219 | ], 220 | ) 221 | .unwrap(); 222 | let conv_layer5 = TransposedConvolutionLayer::new(kernel5, None, 1, Padding::Same); 223 | let target_output5: Array3 = Array::from_shape_vec( 224 | (1, 5, 4), 225 | vec![ 226 | 3.8259244, 6.053112, 5.5964975, 5.0935526, 6.1928873, 8.423359, 7.7538404, 6.2572675, 227 | 7.4473248, 9.735344, 10.054958, 6.237293, 5.549368, 7.910409, 7.076833, 5.7307, 228 | 3.7213798, 5.5014973, 4.9807963, 4.4879804, 229 | ], 230 | ) 231 | .unwrap(); 232 | let current_output5: Array3 = conv_layer5.transposed_convolve(&test_input5); 233 | 234 | assert!( 235 | arr_allclose(¤t_output5, &target_output5), 236 | "{:?} was not equal to {:?}", 237 | current_output5, 238 | target_output5 239 | ); 240 | 241 | let test_input6 = Array::from_shape_vec( 242 | (2, 5, 4), 243 | vec![ 244 | 0.6398819, 0.7132858, 0.90739846, 0.8246988, 0.1319153, 0.6876462, 0.846729, 245 | 0.18134636, 0.9026019, 0.54676574, 0.33139232, 0.83673465, 0.05539654, 0.5554673, 246 | 0.9149804, 0.9791666, 0.9000323, 0.6329186, 0.30872977, 0.30553624, 0.04431259, 247 | 0.5287215, 0.3358437, 0.72178566, 0.72704846, 0.85191244, 0.07464863, 0.867838, 248 | 0.5849202, 0.14913943, 0.18325551, 0.40498126, 0.75359416, 0.01821492, 0.5917236, 249 | 0.91982275, 0.5287654, 0.7151285, 0.38420153, 0.517026, 250 | ], 251 | ) 252 | .unwrap(); 253 | 254 | let kernel6: Array4 = Array::from_shape_vec( 255 | (2, 1, 5, 5), 256 | vec![ 257 | 0.5150141, 0.26934662, 0.7356045, 0.97902703, 0.6362754, 0.27803296, 0.32063982, 258 | 0.83432084, 0.78711736, 0.28247103, 0.7812688, 0.5270146, 0.4719829, 0.47535485, 259 | 0.23715672, 0.4404677, 0.90286416, 0.66611385, 0.02649495, 0.3193976, 0.16608049, 260 | 0.5121102, 0.17436478, 0.313714, 0.47244504, 0.9906781, 0.9064671, 0.29454133, 261 | 0.99321055, 0.12515199, 0.17643407, 0.16320723, 0.48754972, 0.53866875, 0.6358971, 262 | 0.8935865, 0.82249755, 0.92702305, 0.10556214, 0.28283548, 0.14977625, 0.53714937, 263 | 0.06378802, 0.4497216, 0.79292244, 0.5300166, 0.687625, 0.4744624, 0.73655206, 264 | 0.42605233, 265 | ], 266 | ) 267 | .unwrap(); 268 | let conv_layer6 = TransposedConvolutionLayer::new(kernel6, None, 1, Padding::Same); 269 | let target_output6: Array3 = Array::from_shape_vec( 270 | (1, 5, 4), 271 | vec![ 272 | 4.7064695, 8.149392, 6.9905963, 5.4687176, 7.183258, 10.105035, 8.660454, 7.4568315, 273 | 7.718365, 10.839016, 11.462591, 7.8528314, 6.6298823, 8.712183, 8.973379, 5.9587235, 274 | 4.4787836, 6.578704, 6.2997336, 3.255145, 275 | ], 276 | ) 277 | .unwrap(); 278 | let current_output6: Array3 = conv_layer6.transposed_convolve(&test_input6); 279 | 280 | assert!( 281 | arr_allclose(¤t_output6, &target_output6), 282 | "{:?} was not equal to {:?}", 283 | current_output6, 284 | target_output6 285 | ); 286 | 287 | let test_input7 = Array::from_shape_vec( 288 | (2, 4, 3), 289 | vec![ 290 | 0.6592577, 0.3237799, 0.42867732, 0.87574345, 0.70010746, 0.01496939, 0.78416556, 291 | 0.61210716, 0.98323476, 0.9316805, 0.05175687, 0.70876056, 0.52122, 0.68573385, 292 | 0.415587, 0.87644994, 0.73337436, 0.5451011, 0.20423086, 0.5554448, 0.48578474, 293 | 0.2764571, 0.9192946, 0.8920509, 294 | ], 295 | ) 296 | .unwrap(); 297 | 298 | let kernel7: Array4 = Array::from_shape_vec( 299 | (2, 1, 3, 3), 300 | vec![ 301 | 0.34320405, 0.4283556, 0.3897719, 0.06360459, 0.729173, 0.19460331, 0.8634254, 302 | 0.6576018, 0.9657459, 0.22873744, 0.47843248, 0.22157177, 0.9162307, 0.02157675, 303 | 0.44649428, 0.2792192, 0.685827, 0.75294507, 304 | ], 305 | ) 306 | .unwrap(); 307 | let conv_layer7 = TransposedConvolutionLayer::new(kernel7, None, 1, Padding::Same); 308 | let target_output7: Array3 = Array::from_shape_vec( 309 | (1, 4, 3), 310 | vec![ 311 | 2.3433251, 2.336067, 1.3933153, 3.406716, 5.114383, 2.8975387, 3.8696904, 5.1775703, 312 | 3.6608071, 2.8702347, 3.9038558, 2.945689, 313 | ], 314 | ) 315 | .unwrap(); 316 | let current_output7: Array3 = conv_layer7.transposed_convolve(&test_input7); 317 | 318 | assert!( 319 | arr_allclose(¤t_output7, &target_output7), 320 | "{:?} was not equal to {:?}", 321 | current_output7, 322 | target_output7 323 | ); 324 | 325 | let test_input8 = Array::from_shape_vec( 326 | (2, 4, 3), 327 | vec![ 328 | 0.7919586, 0.22560763, 0.88778716, 0.80092114, 0.00003577, 0.9737877, 0.9146247, 329 | 0.13869576, 0.7059647, 0.0936791, 0.2647747, 0.52025, 0.0330946, 0.17754778, 330 | 0.41998118, 0.02919354, 0.82382417, 0.4156855, 0.49590722, 0.69720376, 0.7155315, 331 | 0.40795937, 0.15397236, 0.02265206, 332 | ], 333 | ) 334 | .unwrap(); 335 | 336 | let kernel8: Array4 = Array::from_shape_vec( 337 | (2, 1, 3, 3), 338 | vec![ 339 | 0.7090415, 0.75877124, 0.67035425, 0.39483562, 0.86222315, 0.9993058, 0.84902793, 340 | 0.26427644, 0.86764544, 0.24274233, 0.69865257, 0.5739991, 0.7620597, 0.29289714, 341 | 0.88402116, 0.8942853, 0.7757528, 0.16431308, 342 | ], 343 | ) 344 | .unwrap(); 345 | let conv_layer8 = TransposedConvolutionLayer::new(kernel8, None, 1, Padding::Same); 346 | let target_output8: Array3 = Array::from_shape_vec( 347 | (1, 4, 3), 348 | vec![ 349 | 1.7450328, 3.6583858, 2.773089, 3.2202752, 5.9523973, 4.003776, 3.0722237, 6.0173364, 350 | 2.9648824, 1.7898118, 3.642166, 1.832456, 351 | ], 352 | ) 353 | .unwrap(); 354 | let current_output8: Array3 = conv_layer8.transposed_convolve(&test_input8); 355 | 356 | assert!( 357 | arr_allclose(¤t_output8, &target_output8), 358 | "{:?} was not equal to {:?}", 359 | current_output8, 360 | target_output8 361 | ); 362 | 363 | let test_input9 = Array::from_shape_vec( 364 | (2, 4, 3), 365 | vec![ 366 | 0.48939565, 0.56055367, 0.46453193, 0.9573795, 0.8899508, 0.16098963, 0.63514715, 367 | 0.810474, 0.29953358, 0.46075395, 0.02808736, 0.59781086, 0.8855644, 0.61877847, 368 | 0.9537467, 0.6197168, 0.3379795, 0.13108306, 0.30692965, 0.33537498, 0.01351478, 369 | 0.45529485, 0.6626128, 0.9497022, 370 | ], 371 | ) 372 | .unwrap(); 373 | 374 | let kernel9: Array4 = Array::from_shape_vec( 375 | (2, 1, 3, 3), 376 | vec![ 377 | 0.7403127, 0.06039312, 0.08292098, 0.80679554, 0.1354166, 0.2560622, 0.80891573, 378 | 0.97702754, 0.4124968, 0.4359102, 0.8733311, 0.1396195, 0.52047473, 0.88564533, 379 | 0.29781443, 0.21130918, 0.83071965, 0.3337948, 380 | ], 381 | ) 382 | .unwrap(); 383 | let conv_layer9 = TransposedConvolutionLayer::new(kernel9, None, 1, Padding::Same); 384 | let target_output9: Array3 = Array::from_shape_vec( 385 | (1, 4, 3), 386 | vec![ 387 | 3.0300868, 2.5753088, 1.4805901, 4.4230227, 3.8491464, 2.2942827, 4.1628747, 4.3581924, 388 | 2.0663955, 2.4351592, 3.5008845, 1.8767245, 389 | ], 390 | ) 391 | .unwrap(); 392 | let current_output9: Array3 = conv_layer9.transposed_convolve(&test_input9); 393 | 394 | assert!( 395 | arr_allclose(¤t_output9, &target_output9), 396 | "{:?} was not equal to {:?}", 397 | current_output9, 398 | target_output9 399 | ); 400 | 401 | let test_input10 = Array::from_shape_vec( 402 | (2, 4, 3), 403 | vec![ 404 | 0.9783562, 0.42070097, 0.55396014, 0.16172563, 0.7634967, 0.36205766, 0.48491392, 405 | 0.4980414, 0.6001484, 0.65126085, 0.3691911, 0.11615072, 0.26983073, 0.8690525, 406 | 0.7446416, 0.471613, 0.72900707, 0.842964, 0.21457537, 0.938401, 0.41665936, 407 | 0.05351101, 0.43856344, 0.7253113, 408 | ], 409 | ) 410 | .unwrap(); 411 | 412 | let kernel10: Array4 = Array::from_shape_vec( 413 | (2, 1, 5, 5), 414 | vec![ 415 | 0.8520324, 0.4151233, 0.35101932, 0.7388761, 0.3592986, 0.6741479, 0.9965808, 416 | 0.3117034, 0.8605703, 0.94308543, 0.8192489, 0.31478795, 0.750324, 0.36604244, 417 | 0.78454614, 0.039194, 0.23696694, 0.2418382, 0.24788351, 0.5622223, 0.15428482, 418 | 0.50688076, 0.37682602, 0.44385925, 0.00051633, 0.53422695, 0.5675659, 0.53845215, 419 | 0.2130759, 0.86445093, 0.432881, 0.6255918, 0.3942565, 0.07226853, 0.09869356, 420 | 0.32627374, 0.8543963, 0.85603166, 0.25422713, 0.24929269, 0.36793318, 0.39957172, 421 | 0.9079535, 0.865758, 0.82237726, 0.60374194, 0.7658025, 0.0215734, 0.473204, 0.6594781, 422 | ], 423 | ) 424 | .unwrap(); 425 | let conv_layer10 = TransposedConvolutionLayer::new(kernel10, None, 1, Padding::Same); 426 | let target_output10: Array3 = Array::from_shape_vec( 427 | (1, 4, 3), 428 | vec![ 429 | 6.358123, 5.4534817, 4.9795165, 6.234275, 7.140948, 6.5452733, 6.444521, 6.6835814, 430 | 6.1061664, 3.9202785, 4.5752883, 4.536955, 431 | ], 432 | ) 433 | .unwrap(); 434 | let current_output10: Array3 = conv_layer10.transposed_convolve(&test_input10); 435 | 436 | assert!( 437 | arr_allclose(¤t_output10, &target_output10), 438 | "{:?} was not equal to {:?}", 439 | current_output10, 440 | target_output10 441 | ); 442 | 443 | let test_input11 = Array::from_shape_vec( 444 | (2, 4, 3), 445 | vec![ 446 | 0.86392707, 0.8566667, 0.48541847, 0.54855824, 0.5860555, 0.158874, 0.10558709, 447 | 0.19664533, 0.39609066, 0.27843866, 0.49128443, 0.75033015, 0.35650545, 0.4687038, 448 | 0.6159849, 0.2547001, 0.17250927, 0.07203952, 0.8724657, 0.14018923, 0.12091745, 449 | 0.4770398, 0.43290496, 0.6356798, 450 | ], 451 | ) 452 | .unwrap(); 453 | 454 | let kernel11: Array4 = Array::from_shape_vec( 455 | (2, 1, 5, 5), 456 | vec![ 457 | 0.7619444, 0.2383507, 0.95481586, 0.49641547, 0.7066018, 0.10666513, 0.06810815, 458 | 0.79673207, 0.29373887, 0.55241185, 0.23906927, 0.18915932, 0.02753429, 0.1438574, 459 | 0.5419741, 0.71984386, 0.12837073, 0.55637455, 0.14355855, 0.10225686, 0.8755492, 460 | 0.62895286, 0.21388301, 0.30016333, 0.49605927, 0.8245107, 0.4528765, 0.6836941, 461 | 0.28745723, 0.5031258, 0.26911622, 0.92149794, 0.7165893, 0.04745785, 0.55642486, 462 | 0.34654748, 0.83168656, 0.8410765, 0.7668131, 0.84094906, 0.6012723, 0.9256995, 463 | 0.66010624, 0.43380973, 0.62281454, 0.45852852, 0.61445725, 0.524579, 0.06486581, 464 | 0.14233574, 465 | ], 466 | ) 467 | .unwrap(); 468 | let conv_layer11 = TransposedConvolutionLayer::new(kernel11, None, 1, Padding::Same); 469 | let target_output11: Array3 = Array::from_shape_vec( 470 | (1, 4, 3), 471 | vec![ 472 | 3.2689705, 2.9965467, 3.697768, 5.453937, 4.1977415, 5.072977, 5.0462627, 4.5375757, 473 | 4.380199, 3.3067229, 2.7536871, 3.0206835, 474 | ], 475 | ) 476 | .unwrap(); 477 | let current_output11: Array3 = conv_layer11.transposed_convolve(&test_input11); 478 | 479 | assert!( 480 | arr_allclose(¤t_output11, &target_output11), 481 | "{:?} was not equal to {:?}", 482 | current_output11, 483 | target_output11 484 | ); 485 | 486 | let test_input12 = Array::from_shape_vec( 487 | (2, 4, 3), 488 | vec![ 489 | 0.13930365, 0.5838787, 0.45914945, 0.30819193, 0.70949817, 0.8803759, 0.6927248, 490 | 0.50569826, 0.7358748, 0.58652496, 0.85065925, 0.3247378, 0.6760192, 0.4483504, 491 | 0.59195757, 0.37883574, 0.7796499, 0.32748002, 0.44507304, 0.7488422, 0.14428215, 492 | 0.8785777, 0.15468395, 0.6638725, 493 | ], 494 | ) 495 | .unwrap(); 496 | 497 | let kernel12: Array4 = Array::from_shape_vec( 498 | (2, 1, 5, 5), 499 | vec![ 500 | 0.30651525, 0.9492515, 0.55669755, 0.08302987, 0.45890567, 0.05946975, 0.9099885, 501 | 0.30235246, 0.24214035, 0.42138377, 0.38782486, 0.21880285, 0.9182205, 0.85290706, 502 | 0.5070326, 0.5272234, 0.71761, 0.560904, 0.22129431, 0.2624427, 0.6557088, 0.00633, 503 | 0.6603214, 0.45500925, 0.6523986, 0.14666249, 0.32440838, 0.2805719, 0.42671442, 504 | 0.74477625, 0.9118973, 0.5335283, 0.36968935, 0.5693756, 0.36426732, 0.11819325, 505 | 0.95361024, 0.13343851, 0.5940242, 0.7282295, 0.9320007, 0.46956548, 0.5087874, 506 | 0.7952918, 0.73709416, 0.3358689, 0.03185032, 0.27057356, 0.26166973, 0.5843129, 507 | ], 508 | ) 509 | .unwrap(); 510 | let conv_layer12 = TransposedConvolutionLayer::new(kernel12, None, 1, Padding::Same); 511 | let target_output12: Array3 = Array::from_shape_vec( 512 | (1, 4, 3), 513 | vec![ 514 | 4.1474743, 5.034623, 4.559716, 6.4944124, 6.5415416, 6.6377025, 6.6090937, 6.04299, 515 | 6.581129, 4.068395, 5.2305527, 5.464061, 516 | ], 517 | ) 518 | .unwrap(); 519 | let current_output12: Array3 = conv_layer12.transposed_convolve(&test_input12); 520 | 521 | assert!( 522 | arr_allclose(¤t_output12, &target_output12), 523 | "{:?} was not equal to {:?}", 524 | current_output12, 525 | target_output12 526 | ); 527 | 528 | let test_input13 = Array::from_shape_vec( 529 | (2, 6, 6), 530 | vec![ 531 | 0.5337139, 0.19571388, 0.1192536, 0.75616854, 0.97167397, 0.71154076, 0.26672947, 532 | 0.07128377, 0.3383284, 0.8008563, 0.9504947, 0.5971434, 0.7847773, 0.92326206, 533 | 0.24163638, 0.12968542, 0.5954836, 0.6734995, 0.08627631, 0.7485715, 0.8782484, 534 | 0.6863155, 0.7264353, 0.08944888, 0.68172634, 0.4284322, 0.48237634, 0.6856524, 535 | 0.04009921, 0.0687674, 0.9063645, 0.42620143, 0.61030614, 0.10040135, 0.50107217, 536 | 0.763057, 0.25769317, 0.8816114, 0.7770131, 0.23345616, 0.62762326, 0.15315741, 537 | 0.73589575, 0.2752067, 0.85641515, 0.7439484, 0.6581306, 0.4056261, 0.13899311, 538 | 0.26460668, 0.9179794, 0.96651816, 0.89336777, 0.18948041, 0.9371178, 0.7124811, 539 | 0.9657549, 0.6203675, 0.32684848, 0.11439406, 0.07654371, 0.95680344, 0.9728639, 540 | 0.6055871, 0.00439729, 0.8525613, 0.3167563, 0.13499211, 0.68338645, 0.7955047, 541 | 0.21689218, 0.2826704, 542 | ], 543 | ) 544 | .unwrap(); 545 | 546 | let kernel13: Array4 = Array::from_shape_vec( 547 | (2, 1, 3, 3), 548 | vec![ 549 | 0.12328367, 0.6890043, 0.5906649, 0.41700745, 0.30266365, 0.5716598, 0.12726787, 550 | 0.35587692, 0.16889893, 0.3114892, 0.1309899, 0.24026695, 0.55435276, 0.34375766, 551 | 0.4225387, 0.37165096, 0.6202945, 0.6069533, 552 | ], 553 | ) 554 | .unwrap(); 555 | let conv_layer13 = TransposedConvolutionLayer::new(kernel13, None, 1, Padding::Same); 556 | let target_output13: Array3 = Array::from_shape_vec( 557 | (1, 6, 6), 558 | vec![ 559 | 1.1951423, 1.984751, 2.0163236, 2.835759, 3.0152307, 2.2727869, 1.9734954, 3.8459177, 560 | 3.7884746, 4.008124, 3.8901417, 3.0901344, 1.9761584, 3.8308418, 4.450101, 4.8263526, 561 | 4.0602293, 2.595211, 2.4673147, 4.104005, 4.5204506, 4.5434194, 3.470886, 1.8036022, 562 | 2.6743417, 4.5944138, 4.5940104, 3.5265627, 2.9766128, 1.67339, 1.3359864, 2.791241, 563 | 2.9437308, 2.5690227, 1.9206154, 1.2689613, 564 | ], 565 | ) 566 | .unwrap(); 567 | let current_output13: Array3 = conv_layer13.transposed_convolve(&test_input13); 568 | 569 | assert!( 570 | arr_allclose(¤t_output13, &target_output13), 571 | "{:?} was not equal to {:?}", 572 | current_output13, 573 | target_output13 574 | ); 575 | 576 | let test_input14 = Array::from_shape_vec( 577 | (2, 6, 6), 578 | vec![ 579 | 0.05756181, 0.0153275, 0.8497578, 0.79796964, 0.4071996, 0.19325097, 0.04944435, 580 | 0.59729165, 0.39304134, 0.679002, 0.6126854, 0.67232877, 0.7156292, 0.09058711, 581 | 0.33372015, 0.8941644, 0.30899736, 0.3348802, 0.6412177, 0.26750943, 0.03709449, 582 | 0.56803024, 0.8756273, 0.5771133, 0.56024176, 0.5868575, 0.75167763, 0.36936092, 583 | 0.37185487, 0.35425207, 0.24336162, 0.01027582, 0.2671537, 0.83992416, 0.49819484, 584 | 0.7831867, 0.70054483, 0.8701863, 0.49413827, 0.84670645, 0.8069649, 0.093459, 585 | 0.93408287, 0.8206956, 0.70209366, 0.32817394, 0.744203, 0.39091238, 0.43911496, 586 | 0.92919487, 0.29325068, 0.62813336, 0.8131431, 0.19520658, 0.47931832, 0.42483747, 587 | 0.47526902, 0.28184402, 0.09741595, 0.45691565, 0.44086096, 0.9638409, 0.33859608, 588 | 0.26381832, 0.29839757, 0.68755937, 0.09156765, 0.58584654, 0.04617033, 0.14705709, 589 | 0.81179595, 0.16751295, 590 | ], 591 | ) 592 | .unwrap(); 593 | 594 | let kernel14: Array4 = Array::from_shape_vec( 595 | (2, 1, 3, 3), 596 | vec![ 597 | 0.06864621, 0.6308731, 0.38993102, 0.3644169, 0.68174505, 0.272409, 0.23609333, 598 | 0.21866773, 0.80119157, 0.01742986, 0.49270406, 0.37920174, 0.90869635, 0.8191268, 599 | 0.7636344, 0.7827788, 0.9694966, 0.79533535, 600 | ], 601 | ) 602 | .unwrap(); 603 | let conv_layer14 = TransposedConvolutionLayer::new(kernel14, None, 1, Padding::Same); 604 | let target_output14: Array3 = Array::from_shape_vec( 605 | (1, 6, 6), 606 | vec![ 607 | 1.9561242, 3.22645, 3.9032784, 3.7926514, 3.1532838, 2.07332, 3.8290508, 5.613692, 608 | 5.213581, 6.2217684, 5.574827, 3.350961, 4.091234, 4.913117, 4.9805703, 5.111314, 609 | 5.000969, 3.6696777, 3.2719898, 4.9563117, 4.41199, 4.2285676, 4.637951, 3.057804, 610 | 3.0429473, 4.4504833, 3.9135225, 3.1984298, 4.1672177, 3.5607905, 2.2199647, 3.0671887, 611 | 3.132947, 3.2497768, 3.2815356, 2.706079, 612 | ], 613 | ) 614 | .unwrap(); 615 | let current_output14: Array3 = conv_layer14.transposed_convolve(&test_input14); 616 | 617 | assert!( 618 | arr_allclose(¤t_output14, &target_output14), 619 | "{:?} was not equal to {:?}", 620 | current_output14, 621 | target_output14 622 | ); 623 | 624 | let test_input15 = Array::from_shape_vec( 625 | (2, 6, 6), 626 | vec![ 627 | 0.00732174, 0.8372113, 0.9449071, 0.7670253, 0.50964797, 0.7843069, 0.7365313, 628 | 0.71123123, 0.89694643, 0.9544987, 0.16811895, 0.97018677, 0.25394833, 0.5585159, 629 | 0.51973414, 0.4357872, 0.29419306, 0.5180376, 0.80581653, 0.7190028, 0.52249014, 630 | 0.5252596, 0.33191478, 0.3388958, 0.18619475, 0.6839338, 0.72234726, 0.25884646, 631 | 0.7980101, 0.36219832, 0.7046317, 0.9284394, 0.468965, 0.23158234, 0.32747796, 632 | 0.9097073, 0.7440483, 0.03255624, 0.30697423, 0.6246821, 0.1907446, 0.9843364, 633 | 0.4038572, 0.5747709, 0.65160054, 0.6822517, 0.9840877, 0.18341686, 0.5288406, 634 | 0.92906404, 0.87780553, 0.9090181, 0.82569945, 0.20383635, 0.7166643, 0.17061436, 635 | 0.91526777, 0.08598401, 0.70100874, 0.40537685, 0.5971982, 0.92796123, 0.15859716, 636 | 0.7859081, 0.8564488, 0.35816404, 0.79062706, 0.9644883, 0.13154314, 0.31929043, 637 | 0.80048305, 0.07180241, 638 | ], 639 | ) 640 | .unwrap(); 641 | 642 | let kernel15: Array4 = Array::from_shape_vec( 643 | (2, 1, 3, 3), 644 | vec![ 645 | 0.52815664, 0.45786336, 0.91537887, 0.9783313, 0.25753945, 0.42799363, 0.07738555, 646 | 0.9294206, 0.07022415, 0.60058224, 0.6196278, 0.9008346, 0.04155485, 0.82207745, 647 | 0.41282526, 0.9949608, 0.3109271, 0.47614527, 648 | ], 649 | ) 650 | .unwrap(); 651 | let conv_layer15 = TransposedConvolutionLayer::new(kernel15, None, 1, Padding::Same); 652 | let target_output15: Array3 = Array::from_shape_vec( 653 | (1, 6, 6), 654 | vec![ 655 | 2.7422843, 4.074735, 4.5409536, 4.696369, 4.4800787, 2.9063168, 2.8736432, 5.9057508, 656 | 6.9055333, 5.6943765, 6.6837454, 3.4170837, 3.8174345, 6.2248445, 6.001985, 6.423986, 657 | 4.5103583, 3.548554, 4.2490883, 5.579898, 6.4580526, 5.4931355, 4.7543325, 3.7004132, 658 | 4.325874, 6.755368, 4.6352544, 5.119625, 4.4170313, 3.3628173, 3.1147776, 3.5595477, 659 | 3.2768075, 2.4558537, 3.6510553, 1.6757619, 660 | ], 661 | ) 662 | .unwrap(); 663 | let current_output15: Array3 = conv_layer15.transposed_convolve(&test_input15); 664 | 665 | assert!( 666 | arr_allclose(¤t_output15, &target_output15), 667 | "{:?} was not equal to {:?}", 668 | current_output15, 669 | target_output15 670 | ); 671 | 672 | let test_input16 = Array::from_shape_vec( 673 | (2, 6, 6), 674 | vec![ 675 | 0.7615821, 0.35893062, 0.07713356, 0.6123928, 0.62511915, 0.88098776, 0.21521498, 676 | 0.89468116, 0.1842307, 0.10784956, 0.8354833, 0.2630701, 0.7184675, 0.06132602, 677 | 0.06056971, 0.71349823, 0.00944792, 0.07349233, 0.35231608, 0.8533961, 0.17608605, 678 | 0.18067755, 0.51991457, 0.19053963, 0.5045173, 0.7456122, 0.27621788, 0.48649722, 679 | 0.39297876, 0.54849166, 0.3746991, 0.8194284, 0.34880427, 0.79505837, 0.41293454, 680 | 0.890512, 0.12076204, 0.20799449, 0.88948244, 0.24437675, 0.7731474, 0.65954226, 681 | 0.4922565, 0.5211581, 0.97382414, 0.9779564, 0.1754263, 0.4551989, 0.81067127, 682 | 0.7095975, 0.5087082, 0.78796804, 0.03310984, 0.20805901, 0.45704818, 0.9133983, 683 | 0.5689028, 0.04247752, 0.55938286, 0.9835293, 0.04525762, 0.7176199, 0.82096803, 684 | 0.5575717, 0.5744108, 0.9780689, 0.34563604, 0.3324931, 0.8797563, 0.14932355, 685 | 0.1134909, 0.5645703, 686 | ], 687 | ) 688 | .unwrap(); 689 | 690 | let kernel16: Array4 = Array::from_shape_vec( 691 | (2, 1, 5, 5), 692 | vec![ 693 | 0.30096474, 0.4096875, 0.5703153, 0.88738924, 0.6242374, 0.77620894, 0.681929, 694 | 0.24701218, 0.74485576, 0.61556435, 0.63869125, 0.05875124, 0.3210932, 0.96946657, 695 | 0.09463912, 0.6275637, 0.34162354, 0.74390864, 0.8736778, 0.9513908, 0.24045336, 696 | 0.7003475, 0.87750345, 0.16564246, 0.6314963, 0.21785556, 0.7654987, 0.13988015, 697 | 0.03287261, 0.82226163, 0.60896695, 0.13331373, 0.62307173, 0.23316076, 0.9600204, 698 | 0.55291533, 0.29808274, 0.8765127, 0.98263544, 0.27148733, 0.11101003, 0.2754741, 699 | 0.4540411, 0.19701396, 0.08459944, 0.98017716, 0.9800813, 0.62585706, 0.18621969, 700 | 0.5836321, 701 | ], 702 | ) 703 | .unwrap(); 704 | let conv_layer16 = TransposedConvolutionLayer::new(kernel16, None, 1, Padding::Same); 705 | let target_output16: Array3 = Array::from_shape_vec( 706 | (1, 6, 6), 707 | vec![ 708 | 3.9705598, 5.3050566, 7.605779, 7.076497, 5.436322, 5.5418963, 4.9794617, 7.9182315, 709 | 10.010529, 9.220495, 7.237857, 6.230055, 7.5918703, 10.2776, 12.176399, 13.016567, 710 | 10.292295, 6.7530055, 7.7299433, 11.320428, 12.525111, 11.935645, 9.801254, 7.823039, 711 | 6.383092, 8.149623, 10.79394, 9.786787, 7.006672, 6.1559043, 5.0805273, 6.3784103, 712 | 7.1890254, 8.54358, 5.903902, 4.338979, 713 | ], 714 | ) 715 | .unwrap(); 716 | let current_output16: Array3 = conv_layer16.transposed_convolve(&test_input16); 717 | 718 | assert!( 719 | arr_allclose(¤t_output16, &target_output16), 720 | "{:?} was not equal to {:?}", 721 | current_output16, 722 | target_output16 723 | ); 724 | 725 | let test_input17 = Array::from_shape_vec( 726 | (2, 6, 6), 727 | vec![ 728 | 0.17965508, 0.81336415, 0.94554824, 0.28304926, 0.32718146, 0.46701643, 0.91682273, 729 | 0.6142979, 0.19277428, 0.28408903, 0.25507236, 0.9800453, 0.36125833, 0.96792, 730 | 0.33258557, 0.9508625, 0.8424581, 0.8130291, 0.7774976, 0.17691322, 0.9389267, 731 | 0.22419459, 0.33442205, 0.7614585, 0.37563834, 0.70754576, 0.1499497, 0.9873837, 732 | 0.7166968, 0.40278858, 0.22288306, 0.3279577, 0.07722609, 0.598755, 0.8143903, 733 | 0.87905455, 0.83061284, 0.9923988, 0.6196494, 0.536406, 0.06662867, 0.83588266, 734 | 0.28182983, 0.662181, 0.99850965, 0.60448396, 0.12318801, 0.91713256, 0.8540598, 735 | 0.19103892, 0.5929614, 0.9118344, 0.9381113, 0.3421722, 0.8149188, 0.9595595, 736 | 0.02717085, 0.8916309, 0.9755575, 0.27588442, 0.59208006, 0.00008138, 0.9552037, 737 | 0.12578069, 0.01249961, 0.05413999, 0.5153533, 0.40144867, 0.8698811, 0.6468678, 738 | 0.8840439, 0.34315115, 739 | ], 740 | ) 741 | .unwrap(); 742 | 743 | let kernel17: Array4 = Array::from_shape_vec( 744 | (2, 1, 5, 5), 745 | vec![ 746 | 0.52290094, 0.66408885, 0.98115784, 0.85046166, 0.7979905, 0.9444794, 0.6930413, 747 | 0.90735817, 0.91156703, 0.45996496, 0.9045769, 0.17170428, 0.43418503, 0.69308424, 748 | 0.42869946, 0.9244341, 0.41451567, 0.6224708, 0.00536506, 0.33826837, 0.47926104, 749 | 0.0032256, 0.02230139, 0.35960108, 0.48593593, 0.86064774, 0.28908366, 0.4153976, 750 | 0.3468706, 0.2242362, 0.93169004, 0.3198661, 0.5576108, 0.17364207, 0.69407916, 751 | 0.88076496, 0.11111139, 0.19382912, 0.9307082, 0.9188555, 0.10532705, 0.88550454, 752 | 0.20754221, 0.97926295, 0.3472928, 0.71126235, 0.03633394, 0.7482733, 0.28782296, 753 | 0.7382309, 754 | ], 755 | ) 756 | .unwrap(); 757 | let conv_layer17 = TransposedConvolutionLayer::new(kernel17, None, 1, Padding::Same); 758 | let target_output17: Array3 = Array::from_shape_vec( 759 | (1, 6, 6), 760 | vec![ 761 | 6.7204432, 8.805362, 10.175813, 11.789903, 7.794962, 6.417556, 8.538196, 11.673905, 762 | 13.938214, 13.380728, 10.501698, 7.1781416, 9.131038, 12.454089, 14.61664, 15.670159, 763 | 11.072683, 9.545144, 7.6567736, 11.929808, 15.11018, 15.10865, 11.172414, 10.217181, 764 | 6.790306, 8.061532, 12.025834, 10.597757, 9.491959, 7.698024, 3.154765, 6.91362, 765 | 7.122584, 8.005591, 5.3014894, 4.733845, 766 | ], 767 | ) 768 | .unwrap(); 769 | let current_output17: Array3 = conv_layer17.transposed_convolve(&test_input17); 770 | 771 | assert!( 772 | arr_allclose(¤t_output17, &target_output17), 773 | "{:?} was not equal to {:?}", 774 | current_output17, 775 | target_output17 776 | ); 777 | 778 | let test_input18 = Array::from_shape_vec( 779 | (2, 6, 6), 780 | vec![ 781 | 0.60003114, 0.3259743, 0.85578996, 0.14142898, 0.39191914, 0.05517358, 0.6050927, 782 | 0.968827, 0.47373444, 0.76571184, 0.50472075, 0.53850234, 0.05837096, 0.56728834, 783 | 0.3979838, 0.2393961, 0.36507484, 0.01646563, 0.27730468, 0.62646276, 0.94121945, 784 | 0.66457146, 0.8425775, 0.4301739, 0.68211764, 0.6059688, 0.7629583, 0.56038123, 785 | 0.48163053, 0.437191, 0.43973154, 0.29997745, 0.21913478, 0.6611514, 0.209788, 786 | 0.176356, 0.88925517, 0.14793599, 0.36509594, 0.5616029, 0.6617385, 0.30053073, 787 | 0.36820948, 0.62227124, 0.4957459, 0.69923055, 0.3227648, 0.9702412, 0.5679234, 788 | 0.13432135, 0.7430913, 0.19231908, 0.26170298, 0.10480803, 0.00958306, 0.20726651, 789 | 0.20851518, 0.17973869, 0.23704103, 0.1163867, 0.2735905, 0.8537909, 0.40303, 790 | 0.75797635, 0.34342998, 0.74670875, 0.82946825, 0.91931367, 0.9654975, 0.18782942, 791 | 0.10021565, 0.05109123, 792 | ], 793 | ) 794 | .unwrap(); 795 | 796 | let kernel18: Array4 = Array::from_shape_vec( 797 | (2, 1, 5, 5), 798 | vec![ 799 | 0.3069365, 0.10895161, 0.9891213, 0.62504834, 0.507994, 0.47125, 0.84314245, 0.6054993, 800 | 0.92367506, 0.6953895, 0.37023902, 0.67128026, 0.43450755, 0.28869846, 0.27641538, 801 | 0.8369712, 0.6856483, 0.26890948, 0.65425915, 0.84581685, 0.51146317, 0.48995435, 802 | 0.52071315, 0.80564773, 0.670521, 0.3835857, 0.47078377, 0.5100531, 0.82525116, 803 | 0.02126783, 0.9583153, 0.23316967, 0.9487504, 0.9609589, 0.95734006, 0.00943314, 804 | 0.7521432, 0.46106157, 0.84370506, 0.16933449, 0.71423984, 0.5316646, 0.6400151, 805 | 0.9638914, 0.69432026, 0.8581641, 0.70813745, 0.2836983, 0.33953485, 0.8950543, 806 | ], 807 | ) 808 | .unwrap(); 809 | let conv_layer18 = TransposedConvolutionLayer::new(kernel18, None, 1, Padding::Same); 810 | let target_output18: Array3 = Array::from_shape_vec( 811 | (1, 6, 6), 812 | vec![ 813 | 4.576749, 7.3510118, 7.732251, 8.465681, 6.0988774, 4.8326044, 6.5381684, 8.163401, 814 | 10.939853, 9.563825, 8.826987, 5.6376505, 7.7155824, 11.67995, 14.7064905, 14.329108, 815 | 11.008777, 7.9822774, 8.647253, 12.466508, 13.966044, 14.880844, 10.26346, 7.625294, 816 | 7.2697296, 8.787803, 11.676642, 9.9921665, 8.374546, 4.7419577, 4.972109, 7.382642, 817 | 8.619491, 8.545452, 6.1494026, 4.324534, 818 | ], 819 | ) 820 | .unwrap(); 821 | let current_output18: Array3 = conv_layer18.transposed_convolve(&test_input18); 822 | 823 | assert!( 824 | arr_allclose(¤t_output18, &target_output18), 825 | "{:?} was not equal to {:?}", 826 | current_output18, 827 | target_output18 828 | ); 829 | 830 | let test_input19 = Array::from_shape_vec( 831 | (1, 4, 5), 832 | vec![ 833 | 0.20262088, 0.81930757, 0.09556881, 0.22266188, 0.63842744, 0.8304186, 0.12425002, 834 | 0.7582065, 0.09737789, 0.84872884, 0.17490041, 0.37569723, 0.6669188, 0.8970669, 835 | 0.22398192, 0.29424527, 0.56277037, 0.41113177, 0.7882464, 0.24159203, 836 | ], 837 | ) 838 | .unwrap(); 839 | 840 | let kernel19: Array4 = Array::from_shape_vec( 841 | (1, 2, 3, 3), 842 | vec![ 843 | 0.5460915, 0.29690132, 0.3142075, 0.7648111, 0.04421633, 0.9636684, 0.98529965, 844 | 0.26998708, 0.13811645, 0.9724998, 0.41856614, 0.13321315, 0.0937838, 0.4742414, 845 | 0.79873264, 0.1621314, 0.5493846, 0.6481355, 846 | ], 847 | ) 848 | .unwrap(); 849 | let conv_layer19 = TransposedConvolutionLayer::new(kernel19, None, 1, Padding::Same); 850 | let target_output19: Array3 = Array::from_shape_vec( 851 | (2, 4, 5), 852 | vec![ 853 | 0.94997895, 1.0164421, 1.2813909, 1.3208477, 0.5253867, 1.2508075, 2.2596772, 854 | 1.3920242, 2.6846461, 0.68285376, 1.0363826, 2.0745826, 2.124783, 2.3160646, 1.4363759, 855 | 0.8608186, 1.4055815, 2.2791915, 1.1708186, 0.95466244, 0.6413472, 1.4593375, 856 | 1.1492237, 1.2089542, 0.8488372, 1.0881968, 2.2193813, 2.2891695, 1.7013061, 1.1885908, 857 | 1.2649986, 1.7844094, 2.2269819, 2.2812955, 1.5582577, 0.34932196, 0.96835977, 858 | 1.4737439, 1.6862638, 1.4486442, 859 | ], 860 | ) 861 | .unwrap(); 862 | let current_output19: Array3 = conv_layer19.transposed_convolve(&test_input19); 863 | 864 | assert!( 865 | arr_allclose(¤t_output19, &target_output19), 866 | "{:?} was not equal to {:?}", 867 | current_output19, 868 | target_output19 869 | ); 870 | 871 | let test_input20 = Array::from_shape_vec( 872 | (1, 4, 5), 873 | vec![ 874 | 0.05176858, 0.17029643, 0.955536, 0.089831, 0.05664136, 0.9231865, 0.98567015, 875 | 0.01680515, 0.54690886, 0.8593988, 0.6496963, 0.8382851, 0.2400014, 0.36792266, 876 | 0.01756373, 0.6596718, 0.5680892, 0.19350345, 0.4854058, 0.7869872, 877 | ], 878 | ) 879 | .unwrap(); 880 | 881 | let kernel20: Array4 = Array::from_shape_vec( 882 | (1, 2, 3, 3), 883 | vec![ 884 | 0.12335967, 0.14544043, 0.25711668, 0.05608724, 0.18204254, 0.9562488, 0.44598028, 885 | 0.3229758, 0.8930679, 0.24216229, 0.21437034, 0.38366762, 0.64894366, 0.8650639, 886 | 0.7790032, 0.3529938, 0.37360683, 0.26240814, 887 | ], 888 | ) 889 | .unwrap(); 890 | let conv_layer20 = TransposedConvolutionLayer::new(kernel20, None, 1, Padding::Same); 891 | let target_output20: Array3 = Array::from_shape_vec( 892 | (2, 4, 5), 893 | vec![ 894 | 0.27483612, 0.51689416, 0.6651752, 1.1231388, 0.3618226, 0.5139142, 1.9091319, 895 | 1.7728736, 1.1888504, 0.87510055, 1.0690681, 2.2137527, 2.229632, 1.0898181, 1.3602805, 896 | 0.7356457, 1.7030845, 1.595932, 0.65854263, 0.9416864, 0.5918918, 1.3772991, 1.5317694, 897 | 1.1906348, 0.51303786, 1.859991, 2.4843354, 2.0328515, 1.5234054, 1.3591378, 2.0778565, 898 | 2.4252055, 1.934372, 1.3976136, 1.1213385, 1.4779565, 1.6992863, 1.3644505, 1.2879938, 899 | 1.1620346, 900 | ], 901 | ) 902 | .unwrap(); 903 | let current_output20: Array3 = conv_layer20.transposed_convolve(&test_input20); 904 | 905 | assert!( 906 | arr_allclose(¤t_output20, &target_output20), 907 | "{:?} was not equal to {:?}", 908 | current_output20, 909 | target_output20 910 | ); 911 | 912 | let test_input21 = Array::from_shape_vec( 913 | (1, 4, 5), 914 | vec![ 915 | 0.554433, 0.90017563, 0.6817857, 0.95833224, 0.28551313, 0.85106283, 0.70093375, 916 | 0.4791875, 0.5805011, 0.9803199, 0.97033703, 0.48236644, 0.66855663, 0.35855147, 917 | 0.25242767, 0.6717967, 0.26290378, 0.53677225, 0.18097793, 0.07985484, 918 | ], 919 | ) 920 | .unwrap(); 921 | 922 | let kernel21: Array4 = Array::from_shape_vec( 923 | (1, 2, 3, 3), 924 | vec![ 925 | 0.79416144, 0.7215235, 0.42313012, 0.6529702, 0.00384528, 0.08442257, 0.7838985, 926 | 0.980683, 0.36212954, 0.61172485, 0.5263662, 0.8496614, 0.13177402, 0.9175598, 927 | 0.04323238, 0.03087392, 0.03482862, 0.36226457, 928 | ], 929 | ) 930 | .unwrap(); 931 | let conv_layer21 = TransposedConvolutionLayer::new(kernel21, None, 1, Padding::Same); 932 | let target_output21: Array3 = Array::from_shape_vec( 933 | (2, 4, 5), 934 | vec![ 935 | 1.7606362, 1.7418567, 1.807722, 1.6478107, 1.0349542, 2.7935286, 3.2950132, 3.157129, 936 | 2.8353925, 1.0136617, 2.396291, 2.7917762, 2.0984926, 2.1550565, 1.3370345, 1.503971, 937 | 1.7567389, 1.2538215, 0.8897615, 0.3929793, 1.5040944, 2.32497, 1.9936706, 2.2588148, 938 | 1.3126429, 1.7261962, 2.4836714, 1.9070047, 1.8829131, 1.7192292, 1.5196233, 1.9577055, 939 | 1.5867043, 1.2154206, 0.6873595, 0.69974554, 0.7299658, 0.7368338, 0.46226248, 940 | 0.21977791, 941 | ], 942 | ) 943 | .unwrap(); 944 | let current_output21: Array3 = conv_layer21.transposed_convolve(&test_input21); 945 | 946 | assert!( 947 | arr_allclose(¤t_output21, &target_output21), 948 | "{:?} was not equal to {:?}", 949 | current_output21, 950 | target_output21 951 | ); 952 | 953 | let test_input22 = Array::from_shape_vec( 954 | (1, 4, 5), 955 | vec![ 956 | 0.6059544, 0.29194582, 0.34645593, 0.9409913, 0.60953087, 0.8610255, 0.1627552, 957 | 0.8939359, 0.66327935, 0.48094535, 0.06551108, 0.86129206, 0.65446067, 0.9592776, 958 | 0.8000413, 0.21450381, 0.6442533, 0.46971878, 0.84573215, 0.34651902, 959 | ], 960 | ) 961 | .unwrap(); 962 | 963 | let kernel22: Array4 = Array::from_shape_vec( 964 | (1, 2, 5, 5), 965 | vec![ 966 | 0.18830031, 0.57119006, 0.27269888, 0.7915957, 0.21737069, 0.04463017, 0.393406, 967 | 0.8441482, 0.77705365, 0.6315176, 0.8522896, 0.16809769, 0.22305161, 0.773223, 968 | 0.9678148, 0.2237789, 0.8982615, 0.271913, 0.34780893, 0.49277794, 0.14560463, 969 | 0.33638617, 0.1585067, 0.18610527, 0.6963084, 0.9429821, 0.40789625, 0.20056698, 970 | 0.37306353, 0.9367061, 0.7103306, 0.06833509, 0.48335183, 0.45182207, 0.96346164, 971 | 0.00378525, 0.33689302, 0.28288215, 0.75004077, 0.1607512, 0.514175, 0.35879079, 972 | 0.36688787, 0.00774159, 0.15532039, 0.01133456, 0.94680935, 0.052598, 0.73530346, 973 | 0.8096563, 974 | ], 975 | ) 976 | .unwrap(); 977 | let conv_layer22 = TransposedConvolutionLayer::new(kernel22, None, 1, Padding::Same); 978 | let target_output22: Array3 = Array::from_shape_vec( 979 | (2, 4, 5), 980 | vec![ 981 | 1.9433353, 3.4228127, 4.847467, 3.8331764, 3.804578, 2.4241257, 4.0808473, 6.0651836, 982 | 5.2626843, 4.849197, 1.9977381, 3.8111708, 5.463112, 4.9356112, 4.114871, 1.8158009, 983 | 2.7521765, 3.863211, 3.4117572, 2.8816507, 2.3149717, 3.0260105, 4.1155033, 3.2621598, 984 | 3.458694, 2.1111393, 4.085838, 4.2228155, 4.3918104, 3.3376374, 1.9386842, 3.0858266, 985 | 4.766195, 4.0014625, 3.3781059, 1.1585866, 3.0447168, 3.4465826, 2.8690333, 2.4472578, 986 | ], 987 | ) 988 | .unwrap(); 989 | let current_output22: Array3 = conv_layer22.transposed_convolve(&test_input22); 990 | 991 | assert!( 992 | arr_allclose(¤t_output22, &target_output22), 993 | "{:?} was not equal to {:?}", 994 | current_output22, 995 | target_output22 996 | ); 997 | 998 | let test_input23 = Array::from_shape_vec( 999 | (1, 4, 5), 1000 | vec![ 1001 | 0.7097644, 0.37942106, 0.38656324, 0.13763146, 0.99930644, 0.67476803, 0.06055127, 1002 | 0.8463604, 0.4460729, 0.33209085, 0.94373864, 0.5397614, 0.05766413, 0.64256257, 1003 | 0.7357459, 0.09256811, 0.8832945, 0.03911254, 0.782554, 0.4004812, 1004 | ], 1005 | ) 1006 | .unwrap(); 1007 | 1008 | let kernel23: Array4 = Array::from_shape_vec( 1009 | (1, 2, 5, 5), 1010 | vec![ 1011 | 0.80732703, 0.8010379, 0.28867772, 0.7802926, 0.9490529, 0.5395702, 0.1369102, 1012 | 0.4513485, 0.1162144, 0.9589836, 0.24819064, 0.3149064, 0.22322156, 0.39812055, 1013 | 0.45308354, 0.5711523, 0.97817534, 0.06805564, 0.5386646, 0.6699801, 0.1879456, 1014 | 0.8407316, 0.64728755, 0.53731096, 0.63207716, 0.00916763, 0.46754488, 0.8670982, 1015 | 0.1932068, 0.43971533, 0.10441232, 0.46486124, 0.22910197, 0.7679019, 0.23872072, 1016 | 0.03931138, 0.8551502, 0.19610135, 0.8555064, 0.08035298, 0.05671094, 0.9091975, 1017 | 0.27582648, 0.33816597, 0.42012835, 0.7745094, 0.14011143, 0.47505257, 0.98297566, 1018 | 0.5085966, 1019 | ], 1020 | ) 1021 | .unwrap(); 1022 | let conv_layer23 = TransposedConvolutionLayer::new(kernel23, None, 1, Padding::Same); 1023 | let target_output23: Array3 = Array::from_shape_vec( 1024 | (2, 4, 5), 1025 | vec![ 1026 | 1.8947343, 2.4426258, 4.5688577, 2.4065573, 2.2348871, 2.3167987, 3.2224967, 5.3705425, 1027 | 4.3484287, 2.2617307, 2.0183833, 3.9389546, 3.9471571, 4.130721, 2.5504436, 1.5804148, 1028 | 2.606216, 4.1682005, 2.9336529, 1.8431025, 1.8211585, 2.6728508, 2.134366, 3.313332, 1029 | 1.7529054, 1.7467892, 3.7858887, 3.0247731, 4.261167, 2.190734, 2.0637167, 3.4395082, 1030 | 4.9149113, 2.9692075, 2.8052857, 2.513916, 2.0296254, 3.801062, 2.8139968, 2.2223105, 1031 | ], 1032 | ) 1033 | .unwrap(); 1034 | let current_output23: Array3 = conv_layer23.transposed_convolve(&test_input23); 1035 | 1036 | assert!( 1037 | arr_allclose(¤t_output23, &target_output23), 1038 | "{:?} was not equal to {:?}", 1039 | current_output23, 1040 | target_output23 1041 | ); 1042 | 1043 | let test_input24 = Array::from_shape_vec( 1044 | (1, 4, 5), 1045 | vec![ 1046 | 0.22395916, 0.01153891, 0.669607, 0.12918296, 0.55476546, 0.528783, 0.5067741, 1047 | 0.14234911, 0.5152856, 0.6446142, 0.9118427, 0.8193508, 0.989135, 0.7842302, 1048 | 0.59264964, 0.35247177, 0.48483032, 0.22649786, 0.04158387, 0.5814206, 1049 | ], 1050 | ) 1051 | .unwrap(); 1052 | 1053 | let kernel24: Array4 = Array::from_shape_vec( 1054 | (1, 2, 5, 5), 1055 | vec![ 1056 | 0.8653732, 0.4524277, 0.44947338, 0.5519129, 0.79122764, 0.7513198, 0.9315116, 1057 | 0.03313805, 0.41213843, 0.45310238, 0.01799934, 0.4328227, 0.23842481, 0.20688592, 1058 | 0.57869387, 0.9324833, 0.43915927, 0.53123087, 0.5324186, 0.4149537, 0.22217223, 1059 | 0.0080786, 0.65063345, 0.7681623, 0.8421627, 0.27179125, 0.68530315, 0.9955617, 1060 | 0.77906156, 0.18006648, 0.5975193, 0.6555531, 0.9498266, 0.9690994, 0.5122471, 1061 | 0.3840358, 0.8757204, 0.7260429, 0.04691354, 0.40678895, 0.35535267, 0.19270384, 1062 | 0.23519029, 0.6665529, 0.26402712, 0.55168146, 0.08576613, 0.9567203, 0.57141143, 1063 | 0.20602623, 1064 | ], 1065 | ) 1066 | .unwrap(); 1067 | let conv_layer24 = TransposedConvolutionLayer::new(kernel24, None, 1, Padding::Same); 1068 | let target_output24: Array3 = Array::from_shape_vec( 1069 | (2, 4, 5), 1070 | vec![ 1071 | 2.3034997, 3.0933988, 4.260963, 3.136785, 2.3265605, 3.20681, 3.3054733, 4.8378954, 1072 | 3.5454512, 2.238543, 2.1540961, 2.5372102, 4.2754025, 3.433806, 2.7149425, 2.444301, 1073 | 3.225262, 3.6984525, 3.0090983, 2.3562305, 3.0875251, 4.4669847, 5.009917, 4.030303, 1074 | 3.2448783, 3.914193, 4.744957, 5.4094176, 4.953395, 3.425089, 3.40465, 3.7303526, 1075 | 5.131832, 3.3868535, 2.8532045, 2.1192021, 2.936121, 2.9060504, 2.6555126, 2.3799818, 1076 | ], 1077 | ) 1078 | .unwrap(); 1079 | let current_output24: Array3 = conv_layer24.transposed_convolve(&test_input24); 1080 | 1081 | assert!( 1082 | arr_allclose(¤t_output24, &target_output24), 1083 | "{:?} was not equal to {:?}", 1084 | current_output24, 1085 | target_output24 1086 | ); 1087 | 1088 | let test_input25 = Array::from_shape_vec( 1089 | (1, 3, 3), 1090 | vec![ 1091 | 0.75538653, 0.96776, 0.44755465, 0.82362133, 0.29203537, 0.30861592, 0.39762703, 1092 | 0.26186082, 0.9658112, 1093 | ], 1094 | ) 1095 | .unwrap(); 1096 | 1097 | let kernel25: Array4 = Array::from_shape_vec( 1098 | (1, 2, 3, 3), 1099 | vec![ 1100 | 0.27424002, 0.50570756, 0.8114843, 0.5536027, 0.59788686, 0.05054026, 0.28394443, 1101 | 0.6402348, 0.02258505, 0.60607, 0.6834324, 0.8004999, 0.3349493, 0.4295082, 0.37033585, 1102 | 0.0394791, 0.50698835, 0.29841292, 1103 | ], 1104 | ) 1105 | .unwrap(); 1106 | let conv_layer25 = TransposedConvolutionLayer::new(kernel25, None, 1, Padding::Same); 1107 | let target_output25: Array3 = Array::from_shape_vec( 1108 | (2, 3, 3), 1109 | vec![ 1110 | 1.4839896, 1.7652309, 0.7095494, 1.6854146, 1.8707725, 1.2085879, 0.9929357, 1.0045376, 1111 | 0.79486257, 1.3884788, 1.8912537, 0.99531704, 1.3032048, 2.3501618, 1.6260877, 1112 | 0.6875899, 0.98924637, 0.7554121, 1113 | ], 1114 | ) 1115 | .unwrap(); 1116 | let current_output25: Array3 = conv_layer25.transposed_convolve(&test_input25); 1117 | 1118 | assert!( 1119 | arr_allclose(¤t_output25, &target_output25), 1120 | "{:?} was not equal to {:?}", 1121 | current_output25, 1122 | target_output25 1123 | ); 1124 | 1125 | let test_input26 = Array::from_shape_vec( 1126 | (1, 3, 3), 1127 | vec![ 1128 | 0.9388161, 0.84357476, 0.29760668, 0.25654384, 0.71037215, 0.14646496, 0.9906275, 1129 | 0.9552909, 0.7966291, 1130 | ], 1131 | ) 1132 | .unwrap(); 1133 | 1134 | let kernel26: Array4 = Array::from_shape_vec( 1135 | (1, 2, 3, 3), 1136 | vec![ 1137 | 0.6094893, 0.7271988, 0.09518696, 0.3957746, 0.6157527, 0.31079137, 0.46793708, 1138 | 0.8514491, 0.86081386, 0.8208274, 0.2372746, 0.6977758, 0.79387194, 0.9463199, 1139 | 0.99063903, 0.29514915, 0.8795419, 0.741553, 1140 | ], 1141 | ) 1142 | .unwrap(); 1143 | let conv_layer26 = TransposedConvolutionLayer::new(kernel26, None, 1, Padding::Same); 1144 | let target_output26: Array3 = Array::from_shape_vec( 1145 | (2, 3, 3), 1146 | vec![ 1147 | 1.5314665, 1.5592647, 0.61955523, 2.9358315, 3.5152984, 1.9607604, 1.5389049, 1148 | 2.1056056, 1.5236282, 2.202075, 2.4323668, 1.647742, 2.9006052, 4.140433, 2.5852365, 1149 | 2.131136, 3.3760586, 2.355815, 1150 | ], 1151 | ) 1152 | .unwrap(); 1153 | let current_output26: Array3 = conv_layer26.transposed_convolve(&test_input26); 1154 | 1155 | assert!( 1156 | arr_allclose(¤t_output26, &target_output26), 1157 | "{:?} was not equal to {:?}", 1158 | current_output26, 1159 | target_output26 1160 | ); 1161 | 1162 | let test_input27 = Array::from_shape_vec( 1163 | (1, 3, 3), 1164 | vec![ 1165 | 0.03520088, 0.83411527, 0.47134155, 0.26704508, 0.2872401, 0.04289522, 0.583651, 1166 | 0.94414073, 0.5729697, 1167 | ], 1168 | ) 1169 | .unwrap(); 1170 | 1171 | let kernel27: Array4 = Array::from_shape_vec( 1172 | (1, 2, 3, 3), 1173 | vec![ 1174 | 0.72244126, 0.7707775, 0.02079507, 0.22973806, 0.18128674, 0.05054512, 0.6523751, 1175 | 0.27093726, 0.32479268, 0.53774405, 0.08921466, 0.18494032, 0.78120434, 0.15868789, 1176 | 0.81183356, 0.21792394, 0.6620494, 0.6886836, 1177 | ], 1178 | ) 1179 | .unwrap(); 1180 | let conv_layer27 = TransposedConvolutionLayer::new(kernel27, None, 1, Padding::Same); 1181 | let target_output27: Array3 = Array::from_shape_vec( 1182 | (2, 3, 3), 1183 | vec![ 1184 | 0.6113559, 0.5192191, 0.16664428, 1.8000462, 1.774139, 0.88217914, 0.582454, 0.524836, 1185 | 0.25650892, 0.8354865, 0.6272354, 0.8089081, 1.0316247, 1.4753548, 1.352218, 1.0695786, 1186 | 1.4546822, 1.0836248, 1187 | ], 1188 | ) 1189 | .unwrap(); 1190 | let current_output27: Array3 = conv_layer27.transposed_convolve(&test_input27); 1191 | 1192 | assert!( 1193 | arr_allclose(¤t_output27, &target_output27), 1194 | "{:?} was not equal to {:?}", 1195 | current_output27, 1196 | target_output27 1197 | ); 1198 | 1199 | let test_input28 = Array::from_shape_vec( 1200 | (1, 3, 3), 1201 | vec![ 1202 | 0.39724785, 0.59319293, 0.02472531, 0.21891202, 0.5342134, 0.4041879, 0.53426075, 1203 | 0.20461221, 0.29362342, 1204 | ], 1205 | ) 1206 | .unwrap(); 1207 | 1208 | let kernel28: Array4 = Array::from_shape_vec( 1209 | (1, 2, 5, 5), 1210 | vec![ 1211 | 0.7835011, 0.47930118, 0.2895624, 0.7320774, 0.55179524, 0.37774232, 0.04187773, 1212 | 0.23883641, 0.63051957, 0.97746944, 0.6415133, 0.4162999, 0.04763152, 0.75958186, 1213 | 0.13098118, 0.6238241, 0.4102024, 0.5710282, 0.59153587, 0.66274744, 0.6454271, 1214 | 0.8120847, 0.22876394, 0.6206928, 0.19057496, 0.4622738, 0.9772445, 0.17967562, 1215 | 0.27354005, 0.48198408, 0.09082693, 0.6217685, 0.0965838, 0.8904992, 0.5337058, 1216 | 0.17222154, 0.62424076, 0.95776683, 0.998361, 0.150883, 0.28713444, 0.35105017, 1217 | 0.1765902, 0.9674033, 0.65025777, 0.1959308, 0.3119046, 0.55247456, 0.38556734, 1218 | 0.18145509, 1219 | ], 1220 | ) 1221 | .unwrap(); 1222 | let conv_layer28 = TransposedConvolutionLayer::new(kernel28, None, 1, Padding::Same); 1223 | let target_output28: Array3 = Array::from_shape_vec( 1224 | (2, 3, 3), 1225 | vec![ 1226 | 0.99189097, 1.2139368, 1.6807517, 1.2247887, 1.3418759, 1.8033566, 1.483831, 1.5404893, 1227 | 1.3808284, 1.57672, 1.9478678, 1.6736608, 1.1037357, 2.158333, 2.2857265, 1.4415758, 1228 | 1.8492526, 1.6110936, 1229 | ], 1230 | ) 1231 | .unwrap(); 1232 | let current_output28: Array3 = conv_layer28.transposed_convolve(&test_input28); 1233 | 1234 | assert!( 1235 | arr_allclose(¤t_output28, &target_output28), 1236 | "{:?} was not equal to {:?}", 1237 | current_output28, 1238 | target_output28 1239 | ); 1240 | 1241 | let test_input29 = Array::from_shape_vec( 1242 | (1, 3, 3), 1243 | vec![ 1244 | 0.94255227, 0.7036003, 0.3402552, 0.16401337, 0.28418577, 0.5504918, 0.6148071, 1245 | 0.7740251, 0.37459207, 1246 | ], 1247 | ) 1248 | .unwrap(); 1249 | 1250 | let kernel29: Array4 = Array::from_shape_vec( 1251 | (1, 2, 5, 5), 1252 | vec![ 1253 | 0.25677747, 0.80441076, 0.17314304, 0.5946227, 0.30094627, 0.06480476, 0.5798754, 1254 | 0.62549657, 0.6065069, 0.9259787, 0.3677915, 0.69532603, 0.8356569, 0.39276573, 1255 | 0.18570696, 0.61698794, 0.9064707, 0.01547038, 0.2551995, 0.11438051, 0.03861368, 1256 | 0.6524532, 0.36442748, 0.08230381, 0.79969245, 0.27816203, 0.3741886, 0.44356522, 1257 | 0.27386904, 0.4171807, 0.14990133, 0.4750283, 0.6358263, 0.77572674, 0.7072159, 1258 | 0.34310365, 0.36999545, 0.8200852, 0.46510443, 0.9230857, 0.86201537, 0.04691705, 1259 | 0.12260284, 0.89921826, 0.47400048, 0.7492544, 0.05440097, 0.28273952, 0.9184355, 1260 | 0.9064988, 1261 | ], 1262 | ) 1263 | .unwrap(); 1264 | let conv_layer29 = TransposedConvolutionLayer::new(kernel29, None, 1, Padding::Same); 1265 | let target_output29: Array3 = Array::from_shape_vec( 1266 | (2, 3, 3), 1267 | vec![ 1268 | 2.530352, 2.5921295, 2.1144242, 2.2571082, 2.3187785, 2.167787, 2.6052265, 2.250003, 1269 | 1.7666745, 2.1383805, 2.3625872, 2.7974262, 1.68515, 2.6098163, 3.12961, 1.986789, 1270 | 2.350586, 3.2323365, 1271 | ], 1272 | ) 1273 | .unwrap(); 1274 | let current_output29: Array3 = conv_layer29.transposed_convolve(&test_input29); 1275 | 1276 | assert!( 1277 | arr_allclose(¤t_output29, &target_output29), 1278 | "{:?} was not equal to {:?}", 1279 | current_output29, 1280 | target_output29 1281 | ); 1282 | 1283 | let test_input30 = Array::from_shape_vec( 1284 | (1, 3, 3), 1285 | vec![ 1286 | 0.7027802, 0.19125043, 0.8670149, 0.08634489, 0.722654, 0.77540857, 0.00211577, 1287 | 0.6443405, 0.2344423, 1288 | ], 1289 | ) 1290 | .unwrap(); 1291 | 1292 | let kernel30: Array4 = Array::from_shape_vec( 1293 | (1, 2, 5, 5), 1294 | vec![ 1295 | 0.9849179, 0.9893316, 0.87634915, 0.48734385, 0.8760625, 0.53053117, 0.01152504, 1296 | 0.6788874, 0.76156837, 0.4680538, 0.07214947, 0.549209, 0.8383253, 0.00365476, 1297 | 0.9782156, 0.7419056, 0.40790308, 0.00302504, 0.80174476, 0.7689671, 0.50054824, 1298 | 0.16478458, 0.89107823, 0.7763043, 0.73042727, 0.41667554, 0.7348974, 0.71259016, 1299 | 0.93557405, 0.37013116, 0.14954911, 0.33766475, 0.39673004, 0.03940821, 0.2414454, 1300 | 0.9373305, 0.84819096, 0.9272432, 0.8604789, 0.92845494, 0.8023847, 0.06647868, 1301 | 0.12827316, 0.02774826, 0.8982795, 0.1193029, 0.37523487, 0.28379655, 0.0638791, 1302 | 0.08446726, 1303 | ], 1304 | ) 1305 | .unwrap(); 1306 | let conv_layer30 = TransposedConvolutionLayer::new(kernel30, None, 1, Padding::Same); 1307 | let target_output30: Array3 = Array::from_shape_vec( 1308 | (2, 3, 3), 1309 | vec![ 1310 | 2.1053019, 2.0020053, 3.0535119, 1.3818408, 2.3914309, 2.0843809, 2.3346102, 1.9154978, 1311 | 2.2834659, 2.5934947, 2.7028081, 2.7486267, 2.471838, 1.8386153, 2.2877162, 1.8241762, 1312 | 1.3692772, 1.2885038, 1313 | ], 1314 | ) 1315 | .unwrap(); 1316 | let current_output30: Array3 = conv_layer30.transposed_convolve(&test_input30); 1317 | 1318 | assert!( 1319 | arr_allclose(¤t_output30, &target_output30), 1320 | "{:?} was not equal to {:?}", 1321 | current_output30, 1322 | target_output30 1323 | ); 1324 | } 1325 | -------------------------------------------------------------------------------- /tests/conv2d_transpose_stride1_same_bias_automated_test.rs: -------------------------------------------------------------------------------- 1 | // This file has been automatically generated by Jinja2 via the 2 | // script /Users/almico/projects/convolutions-rs/scripts/generate_tests.py. 3 | // Please do not change this file by hand. 4 | #[allow(unused_imports)] 5 | use convolutions_rs::convolutions::*; 6 | #[allow(unused_imports)] 7 | use convolutions_rs::transposed_convolutions::*; 8 | #[allow(unused_imports)] 9 | use convolutions_rs::Padding; 10 | #[allow(unused_imports)] 11 | use ndarray::{array, Array, Array2, Array3, Array4, Dimension}; 12 | 13 | fn arr_allclose(current: &Array, target: &Array) -> bool { 14 | assert_eq!( 15 | current.shape(), 16 | target.shape(), 17 | "\ngiven array had shape {:?}, but target had shape {:?}", 18 | current.shape(), 19 | target.shape() 20 | ); 21 | (current - target).map(|x| (*x as f32).abs()).sum() < 1e-3 22 | } 23 | 24 | #[test] 25 | fn test_py_implementation_random_arrays_conv2d_transpose_stride1_same_bias() { 26 | let test_input1 = Array::from_shape_vec( 27 | (2, 5, 4), 28 | vec![ 29 | 0.17014849, 0.4305688, 0.5715329, 0.06520256, 0.12669589, 0.7501565, 0.9837982, 30 | 0.55574155, 0.04181346, 0.23677547, 0.51154923, 0.02844254, 0.60484785, 0.72306335, 31 | 0.22177844, 0.16487044, 0.46672952, 0.54035133, 0.6922357, 0.27845532, 0.66966337, 32 | 0.41083884, 0.4583148, 0.70402896, 0.6177326, 0.9269775, 0.56033564, 0.9098013, 33 | 0.2697065, 0.24242379, 0.7944849, 0.75231165, 0.9692583, 0.12854727, 0.9148518, 34 | 0.3356524, 0.37189406, 0.55898565, 0.5888119, 0.44166553, 35 | ], 36 | ) 37 | .unwrap(); 38 | 39 | let kernel1: Array4 = Array::from_shape_vec( 40 | (2, 1, 3, 3), 41 | vec![ 42 | 0.9034325, 0.2795916, 0.7567664, 0.85028297, 0.96145767, 0.5566679, 0.84558666, 43 | 0.0474241, 0.23985276, 0.07658575, 0.7197864, 0.13313323, 0.69580543, 0.12692, 44 | 0.38484824, 0.775336, 0.52113837, 0.4364637, 45 | ], 46 | ) 47 | .unwrap(); 48 | let conv_layer1 = TransposedConvolutionLayer::new( 49 | kernel1, 50 | Some(Array::from_shape_vec((1,), vec![0.14352316]).unwrap()), 51 | 1, 52 | Padding::Same, 53 | ); 54 | let target_output1: Array3 = Array::from_shape_vec( 55 | (1, 5, 4), 56 | vec![ 57 | 2.2728443, 3.75373, 3.6355407, 2.4194517, 3.1045563, 4.7545576, 5.199625, 3.306072, 58 | 3.7987893, 4.753667, 5.066922, 2.4152188, 3.0122, 5.330374, 3.8929133, 2.6810534, 59 | 2.7327435, 3.701478, 3.060283, 1.7144849, 60 | ], 61 | ) 62 | .unwrap(); 63 | let current_output1: Array3 = conv_layer1.transposed_convolve(&test_input1); 64 | 65 | assert!( 66 | arr_allclose(¤t_output1, &target_output1), 67 | "{:?} was not equal to {:?}", 68 | current_output1, 69 | target_output1 70 | ); 71 | 72 | let test_input2 = Array::from_shape_vec( 73 | (2, 5, 4), 74 | vec![ 75 | 0.8997107, 0.64410555, 0.04471071, 0.767672, 0.43464628, 0.16569944, 0.18875164, 76 | 0.12285258, 0.2781115, 0.5390728, 0.5066572, 0.97435564, 0.39133722, 0.7964828, 77 | 0.988919, 0.35985747, 0.00756764, 0.53660643, 0.8659267, 0.8576183, 0.81628793, 78 | 0.9480399, 0.45711017, 0.89837223, 0.8462714, 0.70536447, 0.9289133, 0.0067116, 79 | 0.65220493, 0.72789615, 0.00785976, 0.32536873, 0.09833383, 0.1022715, 0.7567798, 80 | 0.23972042, 0.38848338, 0.00744711, 0.8715701, 0.07988323, 81 | ], 82 | ) 83 | .unwrap(); 84 | 85 | let kernel2: Array4 = Array::from_shape_vec( 86 | (2, 1, 3, 3), 87 | vec![ 88 | 0.6283005, 0.8241853, 0.16570753, 0.6487234, 0.14438818, 0.6286194, 0.34163022, 89 | 0.03235205, 0.25922647, 0.59344524, 0.9308157, 0.22162326, 0.91818297, 0.97700953, 90 | 0.18019113, 0.775954, 0.14120784, 0.3426181, 91 | ], 92 | ) 93 | .unwrap(); 94 | let conv_layer2 = TransposedConvolutionLayer::new( 95 | kernel2, 96 | Some(Array::from_shape_vec((1,), vec![0.5417864]).unwrap()), 97 | 1, 98 | Padding::Same, 99 | ); 100 | let target_output2: Array3 = Array::from_shape_vec( 101 | (1, 5, 4), 102 | vec![ 103 | 4.4261913, 4.4449716, 4.1786127, 1.9854674, 4.893525, 5.3289795, 4.791672, 2.3636212, 104 | 3.9498923, 5.1606207, 4.5350504, 2.5437014, 2.8644495, 4.5496287, 5.5903716, 2.9153159, 105 | 1.6553848, 3.1637242, 3.1760325, 2.0061834, 106 | ], 107 | ) 108 | .unwrap(); 109 | let current_output2: Array3 = conv_layer2.transposed_convolve(&test_input2); 110 | 111 | assert!( 112 | arr_allclose(¤t_output2, &target_output2), 113 | "{:?} was not equal to {:?}", 114 | current_output2, 115 | target_output2 116 | ); 117 | 118 | let test_input3 = Array::from_shape_vec( 119 | (2, 5, 4), 120 | vec![ 121 | 0.7768226, 0.34603763, 0.6114103, 0.9716041, 0.5157695, 0.50755495, 0.6659802, 122 | 0.629322, 0.60627973, 0.27978492, 0.28792506, 0.7547703, 0.0509604, 0.10449678, 123 | 0.89887625, 0.6572328, 0.695583, 0.3626411, 0.37613922, 0.9241278, 0.39898983, 124 | 0.6908677, 0.5511301, 0.36253917, 0.36786652, 0.88718724, 0.69587743, 0.4870034, 125 | 0.7135373, 0.9862549, 0.22876498, 0.75677496, 0.5617529, 0.5566727, 0.7035832, 126 | 0.92333794, 0.85647196, 0.36252776, 0.9373231, 0.01684272, 127 | ], 128 | ) 129 | .unwrap(); 130 | 131 | let kernel3: Array4 = Array::from_shape_vec( 132 | (2, 1, 3, 3), 133 | vec![ 134 | 0.34317794, 0.921993, 0.36392415, 0.7462054, 0.7556754, 0.31284246, 0.4031665, 135 | 0.7376267, 0.7926341, 0.36348057, 0.84374106, 0.03665259, 0.23846498, 0.82509995, 136 | 0.97324103, 0.96826524, 0.77620417, 0.8427075, 137 | ], 138 | ) 139 | .unwrap(); 140 | let conv_layer3 = TransposedConvolutionLayer::new( 141 | kernel3, 142 | Some(Array::from_shape_vec((1,), vec![0.42989048]).unwrap()), 143 | 1, 144 | Padding::Same, 145 | ); 146 | let target_output3: Array3 = Array::from_shape_vec( 147 | (1, 5, 4), 148 | vec![ 149 | 3.0516624, 4.379607, 4.7501454, 3.4499035, 5.020159, 6.7702, 6.723853, 5.5876184, 150 | 4.409551, 6.5021152, 7.57589, 5.6318374, 4.831626, 6.155262, 7.060836, 5.256997, 151 | 3.0740163, 4.6250186, 5.567507, 4.6789284, 152 | ], 153 | ) 154 | .unwrap(); 155 | let current_output3: Array3 = conv_layer3.transposed_convolve(&test_input3); 156 | 157 | assert!( 158 | arr_allclose(¤t_output3, &target_output3), 159 | "{:?} was not equal to {:?}", 160 | current_output3, 161 | target_output3 162 | ); 163 | 164 | let test_input4 = Array::from_shape_vec( 165 | (2, 5, 4), 166 | vec![ 167 | 0.76771307, 0.54514444, 0.7878393, 0.21897991, 0.975659, 0.73295325, 0.69994044, 168 | 0.86701024, 0.76087946, 0.623545, 0.14890751, 0.8610666, 0.21943341, 0.1644093, 169 | 0.6089244, 0.8612485, 0.02602104, 0.30423534, 0.5087405, 0.16869895, 0.3329467, 170 | 0.179494, 0.33126613, 0.11996287, 0.21606164, 0.87119263, 0.33579683, 0.2313126, 171 | 0.2576527, 0.3748985, 0.30113342, 0.8142977, 0.423247, 0.8179498, 0.92175007, 172 | 0.21606776, 0.19149332, 0.4240341, 0.14780204, 0.46443272, 173 | ], 174 | ) 175 | .unwrap(); 176 | 177 | let kernel4: Array4 = Array::from_shape_vec( 178 | (2, 1, 5, 5), 179 | vec![ 180 | 0.00207204, 0.359202, 0.9019851, 0.21363449, 0.6390296, 0.5977058, 0.81350476, 181 | 0.3685356, 0.90738845, 0.7828945, 0.9812083, 0.6726924, 0.46724817, 0.17302093, 182 | 0.39839587, 0.78055173, 0.8576361, 0.7335981, 0.52934057, 0.9529279, 0.78965247, 183 | 0.46750203, 0.08403921, 0.73126566, 0.34934825, 0.3459232, 0.54770446, 0.68714255, 184 | 0.29283327, 0.53392637, 0.8850151, 0.6909357, 0.44388366, 0.9043074, 0.9949724, 185 | 0.64743847, 0.36691284, 0.5396585, 0.5372604, 0.47547424, 0.44373918, 0.34716287, 186 | 0.21774843, 0.9437953, 0.51856595, 0.15765554, 0.9350713, 0.86233217, 0.81734437, 187 | 0.6566154, 188 | ], 189 | ) 190 | .unwrap(); 191 | let conv_layer4 = TransposedConvolutionLayer::new( 192 | kernel4, 193 | Some(Array::from_shape_vec((1,), vec![0.3879487]).unwrap()), 194 | 1, 195 | Padding::Same, 196 | ); 197 | let target_output4: Array3 = Array::from_shape_vec( 198 | (1, 5, 4), 199 | vec![ 200 | 6.1127706, 6.8170853, 7.4482455, 6.028275, 7.437309, 9.819859, 10.352055, 7.2691493, 201 | 8.314576, 11.478379, 11.642733, 9.431056, 6.9541187, 9.945222, 9.60607, 7.333007, 202 | 4.114229, 6.4558897, 6.7533927, 5.253201, 203 | ], 204 | ) 205 | .unwrap(); 206 | let current_output4: Array3 = conv_layer4.transposed_convolve(&test_input4); 207 | 208 | assert!( 209 | arr_allclose(¤t_output4, &target_output4), 210 | "{:?} was not equal to {:?}", 211 | current_output4, 212 | target_output4 213 | ); 214 | 215 | let test_input5 = Array::from_shape_vec( 216 | (2, 5, 4), 217 | vec![ 218 | 0.28848994, 0.5814131, 0.06870039, 0.78543746, 0.47194287, 0.12733267, 0.6111727, 219 | 0.25675833, 0.74741316, 0.45714313, 0.9649919, 0.57969916, 0.5208711, 0.93666834, 220 | 0.13247305, 0.7087714, 0.17877895, 0.40767, 0.6906785, 0.9675369, 0.224454, 0.28950995, 221 | 0.75431514, 0.99582964, 0.8120864, 0.9536324, 0.7703749, 0.683158, 0.16371861, 222 | 0.42877796, 0.7300311, 0.9456737, 0.05700503, 0.19623296, 0.44457257, 0.5512328, 223 | 0.15101007, 0.25140873, 0.2031063, 0.5537751, 224 | ], 225 | ) 226 | .unwrap(); 227 | 228 | let kernel5: Array4 = Array::from_shape_vec( 229 | (2, 1, 5, 5), 230 | vec![ 231 | 0.3641559, 0.701937, 0.27669305, 0.85076064, 0.00186597, 0.3316532, 0.15485734, 232 | 0.35000807, 0.42372492, 0.03995521, 0.5067197, 0.38907993, 0.37287286, 0.21294773, 233 | 0.8646356, 0.10154326, 0.75313085, 0.7910046, 0.15368015, 0.25977603, 0.40315583, 234 | 0.635939, 0.25689796, 0.13678746, 0.43744904, 0.6658951, 0.564682, 0.70429957, 235 | 0.67241764, 0.15790685, 0.25059524, 0.6432024, 0.60438925, 0.45131883, 0.96080875, 236 | 0.40131757, 0.60942906, 0.7035094, 0.6473561, 0.05433872, 0.7478619, 0.24740443, 237 | 0.35665986, 0.00020906, 0.23741227, 0.57013196, 0.6398819, 0.7132858, 0.90739846, 238 | 0.8246988, 239 | ], 240 | ) 241 | .unwrap(); 242 | let conv_layer5 = TransposedConvolutionLayer::new( 243 | kernel5, 244 | Some(Array::from_shape_vec((1,), vec![0.1319153]).unwrap()), 245 | 1, 246 | Padding::Same, 247 | ); 248 | let target_output5: Array3 = Array::from_shape_vec( 249 | (1, 5, 4), 250 | vec![ 251 | 4.545073, 7.1819606, 7.0422697, 6.384361, 6.024588, 7.964457, 8.042324, 5.6777563, 252 | 6.2409325, 9.310606, 9.845416, 7.495718, 5.5756636, 7.9975805, 8.258152, 6.578929, 253 | 4.259518, 5.8143234, 5.5409513, 4.766061, 254 | ], 255 | ) 256 | .unwrap(); 257 | let current_output5: Array3 = conv_layer5.transposed_convolve(&test_input5); 258 | 259 | assert!( 260 | arr_allclose(¤t_output5, &target_output5), 261 | "{:?} was not equal to {:?}", 262 | current_output5, 263 | target_output5 264 | ); 265 | 266 | let test_input6 = Array::from_shape_vec( 267 | (2, 5, 4), 268 | vec![ 269 | 0.6876462, 0.846729, 0.18134636, 0.9026019, 0.54676574, 0.33139232, 0.83673465, 270 | 0.05539654, 0.5554673, 0.9149804, 0.9791666, 0.9000323, 0.6329186, 0.30872977, 271 | 0.30553624, 0.04431259, 0.5287215, 0.3358437, 0.72178566, 0.72704846, 0.85191244, 272 | 0.07464863, 0.867838, 0.5849202, 0.14913943, 0.18325551, 0.40498126, 0.75359416, 273 | 0.01821492, 0.5917236, 0.91982275, 0.5287654, 0.7151285, 0.38420153, 0.517026, 274 | 0.5150141, 0.26934662, 0.7356045, 0.97902703, 0.6362754, 275 | ], 276 | ) 277 | .unwrap(); 278 | 279 | let kernel6: Array4 = Array::from_shape_vec( 280 | (2, 1, 5, 5), 281 | vec![ 282 | 0.27803296, 0.32063982, 0.83432084, 0.78711736, 0.28247103, 0.7812688, 0.5270146, 283 | 0.4719829, 0.47535485, 0.23715672, 0.4404677, 0.90286416, 0.66611385, 0.02649495, 284 | 0.3193976, 0.16608049, 0.5121102, 0.17436478, 0.313714, 0.47244504, 0.9906781, 285 | 0.9064671, 0.29454133, 0.99321055, 0.12515199, 0.17643407, 0.16320723, 0.48754972, 286 | 0.53866875, 0.6358971, 0.8935865, 0.82249755, 0.92702305, 0.10556214, 0.28283548, 287 | 0.14977625, 0.53714937, 0.06378802, 0.4497216, 0.79292244, 0.5300166, 0.687625, 288 | 0.4744624, 0.73655206, 0.42605233, 0.6592577, 0.3237799, 0.42867732, 0.87574345, 289 | 0.70010746, 290 | ], 291 | ) 292 | .unwrap(); 293 | let conv_layer6 = TransposedConvolutionLayer::new( 294 | kernel6, 295 | Some(Array::from_shape_vec((1,), vec![0.01496939]).unwrap()), 296 | 1, 297 | Padding::Same, 298 | ); 299 | let target_output6: Array3 = Array::from_shape_vec( 300 | (1, 5, 4), 301 | vec![ 302 | 4.5758953, 6.499373, 6.8871884, 5.5835495, 6.7641497, 8.9234705, 8.30044, 5.1186976, 303 | 8.340484, 11.381234, 10.779814, 8.081835, 7.211056, 9.158911, 7.913366, 6.1600766, 304 | 5.5128183, 7.2840023, 7.1165247, 5.5267043, 305 | ], 306 | ) 307 | .unwrap(); 308 | let current_output6: Array3 = conv_layer6.transposed_convolve(&test_input6); 309 | 310 | assert!( 311 | arr_allclose(¤t_output6, &target_output6), 312 | "{:?} was not equal to {:?}", 313 | current_output6, 314 | target_output6 315 | ); 316 | 317 | let test_input7 = Array::from_shape_vec( 318 | (2, 4, 3), 319 | vec![ 320 | 0.78416556, 0.61210716, 0.98323476, 0.9316805, 0.05175687, 0.70876056, 0.52122, 321 | 0.68573385, 0.415587, 0.87644994, 0.73337436, 0.5451011, 0.20423086, 0.5554448, 322 | 0.48578474, 0.2764571, 0.9192946, 0.8920509, 0.34320405, 0.4283556, 0.3897719, 323 | 0.06360459, 0.729173, 0.19460331, 324 | ], 325 | ) 326 | .unwrap(); 327 | 328 | let kernel7: Array4 = Array::from_shape_vec( 329 | (2, 1, 3, 3), 330 | vec![ 331 | 0.8634254, 0.6576018, 0.9657459, 0.22873744, 0.47843248, 0.22157177, 0.9162307, 332 | 0.02157675, 0.44649428, 0.2792192, 0.685827, 0.75294507, 0.7919586, 0.22560763, 333 | 0.88778716, 0.80092114, 0.00003577, 0.9737877, 334 | ], 335 | ) 336 | .unwrap(); 337 | let conv_layer7 = TransposedConvolutionLayer::new( 338 | kernel7, 339 | Some(Array::from_shape_vec((1,), vec![0.9146247]).unwrap()), 340 | 1, 341 | Padding::Same, 342 | ); 343 | let target_output7: Array3 = Array::from_shape_vec( 344 | (1, 4, 3), 345 | vec![ 346 | 3.0194216, 4.9309516, 3.9434125, 4.475068, 6.2935762, 4.643378, 3.9981217, 6.6151314, 347 | 4.4164543, 3.0761495, 3.2341595, 2.761453, 348 | ], 349 | ) 350 | .unwrap(); 351 | let current_output7: Array3 = conv_layer7.transposed_convolve(&test_input7); 352 | 353 | assert!( 354 | arr_allclose(¤t_output7, &target_output7), 355 | "{:?} was not equal to {:?}", 356 | current_output7, 357 | target_output7 358 | ); 359 | 360 | let test_input8 = Array::from_shape_vec( 361 | (2, 4, 3), 362 | vec![ 363 | 0.13869576, 0.7059647, 0.0936791, 0.2647747, 0.52025, 0.0330946, 0.17754778, 364 | 0.41998118, 0.02919354, 0.82382417, 0.4156855, 0.49590722, 0.69720376, 0.7155315, 365 | 0.40795937, 0.15397236, 0.02265206, 0.7090415, 0.75877124, 0.67035425, 0.39483562, 366 | 0.86222315, 0.9993058, 0.84902793, 367 | ], 368 | ) 369 | .unwrap(); 370 | 371 | let kernel8: Array4 = Array::from_shape_vec( 372 | (2, 1, 3, 3), 373 | vec![ 374 | 0.26427644, 0.86764544, 0.24274233, 0.69865257, 0.5739991, 0.7620597, 0.29289714, 375 | 0.88402116, 0.8942853, 0.7757528, 0.16431308, 0.48939565, 0.56055367, 0.46453193, 376 | 0.9573795, 0.8899508, 0.16098963, 0.63514715, 377 | ], 378 | ) 379 | .unwrap(); 380 | let conv_layer8 = TransposedConvolutionLayer::new( 381 | kernel8, 382 | Some(Array::from_shape_vec((1,), vec![0.810474]).unwrap()), 383 | 1, 384 | Padding::Same, 385 | ); 386 | let target_output8: Array3 = Array::from_shape_vec( 387 | (1, 4, 3), 388 | vec![ 389 | 2.518369, 3.7689269, 2.55937, 3.3983114, 4.7890153, 3.3315058, 4.1069756, 5.8417478, 390 | 3.7552614, 3.5331664, 5.268794, 3.6537457, 391 | ], 392 | ) 393 | .unwrap(); 394 | let current_output8: Array3 = conv_layer8.transposed_convolve(&test_input8); 395 | 396 | assert!( 397 | arr_allclose(¤t_output8, &target_output8), 398 | "{:?} was not equal to {:?}", 399 | current_output8, 400 | target_output8 401 | ); 402 | 403 | let test_input9 = Array::from_shape_vec( 404 | (2, 4, 3), 405 | vec![ 406 | 0.29953358, 0.46075395, 0.02808736, 0.59781086, 0.8855644, 0.61877847, 0.9537467, 407 | 0.6197168, 0.3379795, 0.13108306, 0.30692965, 0.33537498, 0.01351478, 0.45529485, 408 | 0.6626128, 0.9497022, 0.7403127, 0.06039312, 0.08292098, 0.80679554, 0.1354166, 409 | 0.2560622, 0.80891573, 0.97702754, 410 | ], 411 | ) 412 | .unwrap(); 413 | 414 | let kernel9: Array4 = Array::from_shape_vec( 415 | (2, 1, 3, 3), 416 | vec![ 417 | 0.4124968, 0.4359102, 0.8733311, 0.1396195, 0.52047473, 0.88564533, 0.29781443, 418 | 0.21130918, 0.83071965, 0.3337948, 0.9783562, 0.42070097, 0.55396014, 0.16172563, 419 | 0.7634967, 0.36205766, 0.48491392, 0.4980414, 420 | ], 421 | ) 422 | .unwrap(); 423 | let conv_layer9 = TransposedConvolutionLayer::new( 424 | kernel9, 425 | Some(Array::from_shape_vec((1,), vec![0.6001484]).unwrap()), 426 | 1, 427 | Padding::Same, 428 | ); 429 | let target_output9: Array3 = Array::from_shape_vec( 430 | (1, 4, 3), 431 | vec![ 432 | 2.876923, 3.8675203, 2.8912685, 2.9923503, 5.4890633, 4.3787065, 3.4663131, 5.4171104, 433 | 4.9376316, 1.9191582, 3.295831, 2.8758605, 434 | ], 435 | ) 436 | .unwrap(); 437 | let current_output9: Array3 = conv_layer9.transposed_convolve(&test_input9); 438 | 439 | assert!( 440 | arr_allclose(¤t_output9, &target_output9), 441 | "{:?} was not equal to {:?}", 442 | current_output9, 443 | target_output9 444 | ); 445 | 446 | let test_input10 = Array::from_shape_vec( 447 | (2, 4, 3), 448 | vec![ 449 | 0.65126085, 0.3691911, 0.11615072, 0.26983073, 0.8690525, 0.7446416, 0.471613, 450 | 0.72900707, 0.842964, 0.21457537, 0.938401, 0.41665936, 0.05351101, 0.43856344, 451 | 0.7253113, 0.8520324, 0.4151233, 0.35101932, 0.7388761, 0.3592986, 0.6741479, 452 | 0.9965808, 0.3117034, 0.8605703, 453 | ], 454 | ) 455 | .unwrap(); 456 | 457 | let kernel10: Array4 = Array::from_shape_vec( 458 | (2, 1, 5, 5), 459 | vec![ 460 | 0.94308543, 0.8192489, 0.31478795, 0.750324, 0.36604244, 0.78454614, 0.039194, 461 | 0.23696694, 0.2418382, 0.24788351, 0.5622223, 0.15428482, 0.50688076, 0.37682602, 462 | 0.44385925, 0.00051633, 0.53422695, 0.5675659, 0.53845215, 0.2130759, 0.86445093, 463 | 0.432881, 0.6255918, 0.3942565, 0.07226853, 0.09869356, 0.32627374, 0.8543963, 464 | 0.85603166, 0.25422713, 0.24929269, 0.36793318, 0.39957172, 0.9079535, 0.865758, 465 | 0.82237726, 0.60374194, 0.7658025, 0.0215734, 0.473204, 0.6594781, 0.86392707, 466 | 0.8566667, 0.48541847, 0.54855824, 0.5860555, 0.158874, 0.10558709, 0.19664533, 467 | 0.39609066, 468 | ], 469 | ) 470 | .unwrap(); 471 | let conv_layer10 = TransposedConvolutionLayer::new( 472 | kernel10, 473 | Some(Array::from_shape_vec((1,), vec![0.27843866]).unwrap()), 474 | 1, 475 | Padding::Same, 476 | ); 477 | let target_output10: Array3 = Array::from_shape_vec( 478 | (1, 4, 3), 479 | vec![ 480 | 5.251691, 5.3062053, 5.1204453, 7.2942615, 6.7145433, 6.948665, 6.688881, 6.1093373, 481 | 6.2989287, 6.02119, 5.285459, 5.4886856, 482 | ], 483 | ) 484 | .unwrap(); 485 | let current_output10: Array3 = conv_layer10.transposed_convolve(&test_input10); 486 | 487 | assert!( 488 | arr_allclose(¤t_output10, &target_output10), 489 | "{:?} was not equal to {:?}", 490 | current_output10, 491 | target_output10 492 | ); 493 | 494 | let test_input11 = Array::from_shape_vec( 495 | (2, 4, 3), 496 | vec![ 497 | 0.49128443, 0.75033015, 0.35650545, 0.4687038, 0.6159849, 0.2547001, 0.17250927, 498 | 0.07203952, 0.8724657, 0.14018923, 0.12091745, 0.4770398, 0.43290496, 0.6356798, 499 | 0.7619444, 0.2383507, 0.95481586, 0.49641547, 0.7066018, 0.10666513, 0.06810815, 500 | 0.79673207, 0.29373887, 0.55241185, 501 | ], 502 | ) 503 | .unwrap(); 504 | 505 | let kernel11: Array4 = Array::from_shape_vec( 506 | (2, 1, 5, 5), 507 | vec![ 508 | 0.23906927, 0.18915932, 0.02753429, 0.1438574, 0.5419741, 0.71984386, 0.12837073, 509 | 0.55637455, 0.14355855, 0.10225686, 0.8755492, 0.62895286, 0.21388301, 0.30016333, 510 | 0.49605927, 0.8245107, 0.4528765, 0.6836941, 0.28745723, 0.5031258, 0.26911622, 511 | 0.92149794, 0.7165893, 0.04745785, 0.55642486, 0.34654748, 0.83168656, 0.8410765, 512 | 0.7668131, 0.84094906, 0.6012723, 0.9256995, 0.66010624, 0.43380973, 0.62281454, 513 | 0.45852852, 0.61445725, 0.524579, 0.06486581, 0.14233574, 0.13930365, 0.5838787, 514 | 0.45914945, 0.30819193, 0.70949817, 0.8803759, 0.6927248, 0.50569826, 0.7358748, 515 | 0.58652496, 516 | ], 517 | ) 518 | .unwrap(); 519 | let conv_layer11 = TransposedConvolutionLayer::new( 520 | kernel11, 521 | Some(Array::from_shape_vec((1,), vec![0.85065925]).unwrap()), 522 | 1, 523 | Padding::Same, 524 | ); 525 | let target_output11: Array3 = Array::from_shape_vec( 526 | (1, 4, 3), 527 | vec![ 528 | 5.5033226, 4.728496, 3.9279737, 6.732407, 5.838501, 5.7494116, 7.7460895, 6.3400507, 529 | 5.5228577, 5.6832623, 4.2647943, 4.3162546, 530 | ], 531 | ) 532 | .unwrap(); 533 | let current_output11: Array3 = conv_layer11.transposed_convolve(&test_input11); 534 | 535 | assert!( 536 | arr_allclose(¤t_output11, &target_output11), 537 | "{:?} was not equal to {:?}", 538 | current_output11, 539 | target_output11 540 | ); 541 | 542 | let test_input12 = Array::from_shape_vec( 543 | (2, 4, 3), 544 | vec![ 545 | 0.3247378, 0.6760192, 0.4483504, 0.59195757, 0.37883574, 0.7796499, 0.32748002, 546 | 0.44507304, 0.7488422, 0.14428215, 0.8785777, 0.15468395, 0.6638725, 0.30651525, 547 | 0.9492515, 0.55669755, 0.08302987, 0.45890567, 0.05946975, 0.9099885, 0.30235246, 548 | 0.24214035, 0.42138377, 0.38782486, 549 | ], 550 | ) 551 | .unwrap(); 552 | 553 | let kernel12: Array4 = Array::from_shape_vec( 554 | (2, 1, 5, 5), 555 | vec![ 556 | 0.21880285, 0.9182205, 0.85290706, 0.5070326, 0.5272234, 0.71761, 0.560904, 0.22129431, 557 | 0.2624427, 0.6557088, 0.00633, 0.6603214, 0.45500925, 0.6523986, 0.14666249, 558 | 0.32440838, 0.2805719, 0.42671442, 0.74477625, 0.9118973, 0.5335283, 0.36968935, 559 | 0.5693756, 0.36426732, 0.11819325, 0.95361024, 0.13343851, 0.5940242, 0.7282295, 560 | 0.9320007, 0.46956548, 0.5087874, 0.7952918, 0.73709416, 0.3358689, 0.03185032, 561 | 0.27057356, 0.26166973, 0.5843129, 0.5337139, 0.19571388, 0.1192536, 0.75616854, 562 | 0.97167397, 0.71154076, 0.26672947, 0.07128377, 0.3383284, 0.8008563, 0.9504947, 563 | ], 564 | ) 565 | .unwrap(); 566 | let conv_layer12 = TransposedConvolutionLayer::new( 567 | kernel12, 568 | Some(Array::from_shape_vec((1,), vec![0.5971434]).unwrap()), 569 | 1, 570 | Padding::Same, 571 | ); 572 | let target_output12: Array3 = Array::from_shape_vec( 573 | (1, 4, 3), 574 | vec![ 575 | 4.3813686, 5.381424, 5.2795916, 5.547638, 6.756645, 7.0753183, 4.8471546, 5.744134, 576 | 6.6946125, 3.3635855, 4.2019253, 5.2463818, 577 | ], 578 | ) 579 | .unwrap(); 580 | let current_output12: Array3 = conv_layer12.transposed_convolve(&test_input12); 581 | 582 | assert!( 583 | arr_allclose(¤t_output12, &target_output12), 584 | "{:?} was not equal to {:?}", 585 | current_output12, 586 | target_output12 587 | ); 588 | 589 | let test_input13 = Array::from_shape_vec( 590 | (2, 6, 6), 591 | vec![ 592 | 0.7847773, 0.92326206, 0.24163638, 0.12968542, 0.5954836, 0.6734995, 0.08627631, 593 | 0.7485715, 0.8782484, 0.6863155, 0.7264353, 0.08944888, 0.68172634, 0.4284322, 594 | 0.48237634, 0.6856524, 0.04009921, 0.0687674, 0.9063645, 0.42620143, 0.61030614, 595 | 0.10040135, 0.50107217, 0.763057, 0.25769317, 0.8816114, 0.7770131, 0.23345616, 596 | 0.62762326, 0.15315741, 0.73589575, 0.2752067, 0.85641515, 0.7439484, 0.6581306, 597 | 0.4056261, 0.13899311, 0.26460668, 0.9179794, 0.96651816, 0.89336777, 0.18948041, 598 | 0.9371178, 0.7124811, 0.9657549, 0.6203675, 0.32684848, 0.11439406, 0.07654371, 599 | 0.95680344, 0.9728639, 0.6055871, 0.00439729, 0.8525613, 0.3167563, 0.13499211, 600 | 0.68338645, 0.7955047, 0.21689218, 0.2826704, 0.12328367, 0.6890043, 0.5906649, 601 | 0.41700745, 0.30266365, 0.5716598, 0.12726787, 0.35587692, 0.16889893, 0.3114892, 602 | 0.1309899, 0.24026695, 603 | ], 604 | ) 605 | .unwrap(); 606 | 607 | let kernel13: Array4 = Array::from_shape_vec( 608 | (2, 1, 3, 3), 609 | vec![ 610 | 0.55435276, 0.34375766, 0.4225387, 0.37165096, 0.6202945, 0.6069533, 0.05756181, 611 | 0.0153275, 0.8497578, 0.79796964, 0.4071996, 0.19325097, 0.04944435, 0.59729165, 612 | 0.39304134, 0.679002, 0.6126854, 0.67232877, 613 | ], 614 | ) 615 | .unwrap(); 616 | let conv_layer13 = TransposedConvolutionLayer::new( 617 | kernel13, 618 | Some(Array::from_shape_vec((1,), vec![0.7156292]).unwrap()), 619 | 1, 620 | Padding::Same, 621 | ); 622 | let target_output13: Array3 = Array::from_shape_vec( 623 | (1, 6, 6), 624 | vec![ 625 | 3.0364194, 4.135023, 4.1988244, 3.8560987, 3.2704368, 2.4065742, 3.2388349, 5.857635, 626 | 6.8409224, 5.6680775, 4.685433, 3.0304813, 3.2776394, 5.611095, 6.082516, 5.4421124, 627 | 3.8648124, 2.833129, 3.5411034, 5.526158, 5.427699, 4.821819, 4.4624085, 2.9167113, 628 | 2.3763213, 4.9530053, 5.099806, 4.95107, 3.7437892, 2.9491467, 1.9660606, 3.105524, 629 | 3.8293252, 3.7585092, 3.0094833, 2.6510978, 630 | ], 631 | ) 632 | .unwrap(); 633 | let current_output13: Array3 = conv_layer13.transposed_convolve(&test_input13); 634 | 635 | assert!( 636 | arr_allclose(¤t_output13, &target_output13), 637 | "{:?} was not equal to {:?}", 638 | current_output13, 639 | target_output13 640 | ); 641 | 642 | let test_input14 = Array::from_shape_vec( 643 | (2, 6, 6), 644 | vec![ 645 | 0.09058711, 0.33372015, 0.8941644, 0.30899736, 0.3348802, 0.6412177, 0.26750943, 646 | 0.03709449, 0.56803024, 0.8756273, 0.5771133, 0.56024176, 0.5868575, 0.75167763, 647 | 0.36936092, 0.37185487, 0.35425207, 0.24336162, 0.01027582, 0.2671537, 0.83992416, 648 | 0.49819484, 0.7831867, 0.70054483, 0.8701863, 0.49413827, 0.84670645, 0.8069649, 649 | 0.093459, 0.93408287, 0.8206956, 0.70209366, 0.32817394, 0.744203, 0.39091238, 650 | 0.43911496, 0.92919487, 0.29325068, 0.62813336, 0.8131431, 0.19520658, 0.47931832, 651 | 0.42483747, 0.47526902, 0.28184402, 0.09741595, 0.45691565, 0.44086096, 0.9638409, 652 | 0.33859608, 0.26381832, 0.29839757, 0.68755937, 0.09156765, 0.58584654, 0.04617033, 653 | 0.14705709, 0.81179595, 0.16751295, 0.06864621, 0.6308731, 0.38993102, 0.3644169, 654 | 0.68174505, 0.272409, 0.23609333, 0.21866773, 0.80119157, 0.01742986, 0.49270406, 655 | 0.37920174, 0.90869635, 656 | ], 657 | ) 658 | .unwrap(); 659 | 660 | let kernel14: Array4 = Array::from_shape_vec( 661 | (2, 1, 3, 3), 662 | vec![ 663 | 0.8191268, 0.7636344, 0.7827788, 0.9694966, 0.79533535, 0.00732174, 0.8372113, 664 | 0.9449071, 0.7670253, 0.50964797, 0.7843069, 0.7365313, 0.71123123, 0.89694643, 665 | 0.9544987, 0.16811895, 0.97018677, 0.25394833, 666 | ], 667 | ) 668 | .unwrap(); 669 | let conv_layer14 = TransposedConvolutionLayer::new( 670 | kernel14, 671 | Some(Array::from_shape_vec((1,), vec![0.5585159]).unwrap()), 672 | 1, 673 | Padding::Same, 674 | ); 675 | let target_output14: Array3 = Array::from_shape_vec( 676 | (1, 6, 6), 677 | vec![ 678 | 2.8061981, 4.820502, 4.7941384, 4.7061067, 4.98075, 3.249073, 4.8344817, 6.378289, 679 | 6.598789, 6.3814144, 5.5168314, 4.2585955, 4.3449416, 5.7049437, 5.5208097, 6.707863, 680 | 6.5998726, 4.3340974, 5.3226647, 7.086091, 6.708675, 6.408688, 6.724382, 3.280261, 681 | 5.1640215, 6.4173756, 7.1730275, 6.8119073, 7.0603776, 4.7790117, 4.57145, 4.823851, 682 | 5.098828, 4.550021, 4.723699, 3.3401604, 683 | ], 684 | ) 685 | .unwrap(); 686 | let current_output14: Array3 = conv_layer14.transposed_convolve(&test_input14); 687 | 688 | assert!( 689 | arr_allclose(¤t_output14, &target_output14), 690 | "{:?} was not equal to {:?}", 691 | current_output14, 692 | target_output14 693 | ); 694 | 695 | let test_input15 = Array::from_shape_vec( 696 | (2, 6, 6), 697 | vec![ 698 | 0.51973414, 0.4357872, 0.29419306, 0.5180376, 0.80581653, 0.7190028, 0.52249014, 699 | 0.5252596, 0.33191478, 0.3388958, 0.18619475, 0.6839338, 0.72234726, 0.25884646, 700 | 0.7980101, 0.36219832, 0.7046317, 0.9284394, 0.468965, 0.23158234, 0.32747796, 701 | 0.9097073, 0.7440483, 0.03255624, 0.30697423, 0.6246821, 0.1907446, 0.9843364, 702 | 0.4038572, 0.5747709, 0.65160054, 0.6822517, 0.9840877, 0.18341686, 0.5288406, 703 | 0.92906404, 0.87780553, 0.9090181, 0.82569945, 0.20383635, 0.7166643, 0.17061436, 704 | 0.91526777, 0.08598401, 0.70100874, 0.40537685, 0.5971982, 0.92796123, 0.15859716, 705 | 0.7859081, 0.8564488, 0.35816404, 0.79062706, 0.9644883, 0.13154314, 0.31929043, 706 | 0.80048305, 0.07180241, 0.52815664, 0.45786336, 0.91537887, 0.9783313, 0.25753945, 707 | 0.42799363, 0.07738555, 0.9294206, 0.07022415, 0.60058224, 0.6196278, 0.9008346, 708 | 0.04155485, 0.82207745, 709 | ], 710 | ) 711 | .unwrap(); 712 | 713 | let kernel15: Array4 = Array::from_shape_vec( 714 | (2, 1, 3, 3), 715 | vec![ 716 | 0.41282526, 0.9949608, 0.3109271, 0.47614527, 0.7615821, 0.35893062, 0.07713356, 717 | 0.6123928, 0.62511915, 0.88098776, 0.21521498, 0.89468116, 0.1842307, 0.10784956, 718 | 0.8354833, 0.2630701, 0.7184675, 0.06132602, 719 | ], 720 | ) 721 | .unwrap(); 722 | let conv_layer15 = TransposedConvolutionLayer::new( 723 | kernel15, 724 | Some(Array::from_shape_vec((1,), vec![0.06056971]).unwrap()), 725 | 1, 726 | Padding::Same, 727 | ); 728 | let target_output15: Array3 = Array::from_shape_vec( 729 | (1, 6, 6), 730 | vec![ 731 | 1.9354559, 3.979683, 2.7921665, 3.546054, 3.3632855, 2.986937, 3.5969198, 5.1250286, 732 | 4.3075085, 4.6540527, 5.2060523, 4.4156575, 2.8083382, 3.6512413, 4.2165513, 5.2899523, 733 | 4.4260244, 3.8585136, 3.0062366, 4.3764443, 4.726912, 4.8880005, 4.9073653, 3.5602653, 734 | 2.8285027, 4.588989, 5.583103, 3.8970323, 5.5693207, 2.9574742, 2.1510997, 2.9361525, 735 | 2.8176718, 2.5246208, 3.1134243, 2.358273, 736 | ], 737 | ) 738 | .unwrap(); 739 | let current_output15: Array3 = conv_layer15.transposed_convolve(&test_input15); 740 | 741 | assert!( 742 | arr_allclose(¤t_output15, &target_output15), 743 | "{:?} was not equal to {:?}", 744 | current_output15, 745 | target_output15 746 | ); 747 | 748 | let test_input16 = Array::from_shape_vec( 749 | (2, 6, 6), 750 | vec![ 751 | 0.71349823, 0.00944792, 0.07349233, 0.35231608, 0.8533961, 0.17608605, 0.18067755, 752 | 0.51991457, 0.19053963, 0.5045173, 0.7456122, 0.27621788, 0.48649722, 0.39297876, 753 | 0.54849166, 0.3746991, 0.8194284, 0.34880427, 0.79505837, 0.41293454, 0.890512, 754 | 0.12076204, 0.20799449, 0.88948244, 0.24437675, 0.7731474, 0.65954226, 0.4922565, 755 | 0.5211581, 0.97382414, 0.9779564, 0.1754263, 0.4551989, 0.81067127, 0.7095975, 756 | 0.5087082, 0.78796804, 0.03310984, 0.20805901, 0.45704818, 0.9133983, 0.5689028, 757 | 0.04247752, 0.55938286, 0.9835293, 0.04525762, 0.7176199, 0.82096803, 0.5575717, 758 | 0.5744108, 0.9780689, 0.34563604, 0.3324931, 0.8797563, 0.14932355, 0.1134909, 759 | 0.5645703, 0.30096474, 0.4096875, 0.5703153, 0.88738924, 0.6242374, 0.77620894, 760 | 0.681929, 0.24701218, 0.74485576, 0.61556435, 0.63869125, 0.05875124, 0.3210932, 761 | 0.96946657, 0.09463912, 762 | ], 763 | ) 764 | .unwrap(); 765 | 766 | let kernel16: Array4 = Array::from_shape_vec( 767 | (2, 1, 5, 5), 768 | vec![ 769 | 0.6275637, 0.34162354, 0.74390864, 0.8736778, 0.9513908, 0.24045336, 0.7003475, 770 | 0.87750345, 0.16564246, 0.6314963, 0.21785556, 0.7654987, 0.13988015, 0.03287261, 771 | 0.82226163, 0.60896695, 0.13331373, 0.62307173, 0.23316076, 0.9600204, 0.55291533, 772 | 0.29808274, 0.8765127, 0.98263544, 0.27148733, 0.11101003, 0.2754741, 0.4540411, 773 | 0.19701396, 0.08459944, 0.98017716, 0.9800813, 0.62585706, 0.18621969, 0.5836321, 774 | 0.17965508, 0.81336415, 0.94554824, 0.28304926, 0.32718146, 0.46701643, 0.91682273, 775 | 0.6142979, 0.19277428, 0.28408903, 0.25507236, 0.9800453, 0.36125833, 0.96792, 776 | 0.33258557, 777 | ], 778 | ) 779 | .unwrap(); 780 | let conv_layer16 = TransposedConvolutionLayer::new( 781 | kernel16, 782 | Some(Array::from_shape_vec((1,), vec![0.9508625]).unwrap()), 783 | 1, 784 | Padding::Same, 785 | ); 786 | let target_output16: Array3 = Array::from_shape_vec( 787 | (1, 6, 6), 788 | vec![ 789 | 5.350966, 5.539909, 7.988808, 8.704291, 7.810769, 5.42366, 7.3473563, 7.9194345, 790 | 10.856416, 10.792727, 9.997356, 6.5414715, 8.424494, 11.241109, 12.750721, 13.291385, 791 | 12.976979, 9.782504, 9.53326, 12.167869, 13.93993, 13.914165, 14.242102, 9.71659, 792 | 8.778961, 9.369434, 12.641815, 12.276781, 10.91404, 7.2100573, 6.1916404, 7.2183013, 793 | 8.587385, 9.284393, 7.3272343, 5.800124, 794 | ], 795 | ) 796 | .unwrap(); 797 | let current_output16: Array3 = conv_layer16.transposed_convolve(&test_input16); 798 | 799 | assert!( 800 | arr_allclose(¤t_output16, &target_output16), 801 | "{:?} was not equal to {:?}", 802 | current_output16, 803 | target_output16 804 | ); 805 | 806 | let test_input17 = Array::from_shape_vec( 807 | (2, 6, 6), 808 | vec![ 809 | 0.8424581, 0.8130291, 0.7774976, 0.17691322, 0.9389267, 0.22419459, 0.33442205, 810 | 0.7614585, 0.37563834, 0.70754576, 0.1499497, 0.9873837, 0.7166968, 0.40278858, 811 | 0.22288306, 0.3279577, 0.07722609, 0.598755, 0.8143903, 0.87905455, 0.83061284, 812 | 0.9923988, 0.6196494, 0.536406, 0.06662867, 0.83588266, 0.28182983, 0.662181, 813 | 0.99850965, 0.60448396, 0.12318801, 0.91713256, 0.8540598, 0.19103892, 0.5929614, 814 | 0.9118344, 0.9381113, 0.3421722, 0.8149188, 0.9595595, 0.02717085, 0.8916309, 815 | 0.9755575, 0.27588442, 0.59208006, 0.00008138, 0.9552037, 0.12578069, 0.01249961, 816 | 0.05413999, 0.5153533, 0.40144867, 0.8698811, 0.6468678, 0.8840439, 0.34315115, 817 | 0.52290094, 0.66408885, 0.98115784, 0.85046166, 0.7979905, 0.9444794, 0.6930413, 818 | 0.90735817, 0.91156703, 0.45996496, 0.9045769, 0.17170428, 0.43418503, 0.69308424, 819 | 0.42869946, 0.9244341, 820 | ], 821 | ) 822 | .unwrap(); 823 | 824 | let kernel17: Array4 = Array::from_shape_vec( 825 | (2, 1, 5, 5), 826 | vec![ 827 | 0.41451567, 0.6224708, 0.00536506, 0.33826837, 0.47926104, 0.0032256, 0.02230139, 828 | 0.35960108, 0.48593593, 0.86064774, 0.28908366, 0.4153976, 0.3468706, 0.2242362, 829 | 0.93169004, 0.3198661, 0.5576108, 0.17364207, 0.69407916, 0.88076496, 0.11111139, 830 | 0.19382912, 0.9307082, 0.9188555, 0.10532705, 0.88550454, 0.20754221, 0.97926295, 831 | 0.3472928, 0.71126235, 0.03633394, 0.7482733, 0.28782296, 0.7382309, 0.60003114, 832 | 0.3259743, 0.85578996, 0.14142898, 0.39191914, 0.05517358, 0.6050927, 0.968827, 833 | 0.47373444, 0.76571184, 0.50472075, 0.53850234, 0.05837096, 0.56728834, 0.3979838, 834 | 0.2393961, 835 | ], 836 | ) 837 | .unwrap(); 838 | let conv_layer17 = TransposedConvolutionLayer::new( 839 | kernel17, 840 | Some(Array::from_shape_vec((1,), vec![0.36507484]).unwrap()), 841 | 1, 842 | Padding::Same, 843 | ); 844 | let target_output17: Array3 = Array::from_shape_vec( 845 | (1, 6, 6), 846 | vec![ 847 | 3.384909, 5.3650627, 6.984501, 6.952221, 6.286247, 4.1938, 6.2164855, 8.931842, 848 | 11.041458, 11.684279, 9.272891, 7.504865, 7.845093, 11.048375, 14.195403, 14.827918, 849 | 11.553749, 9.662743, 7.258074, 9.461692, 13.202706, 14.349618, 11.879109, 9.826926, 850 | 5.4586234, 8.431676, 11.006513, 11.547409, 10.154126, 8.025545, 5.556083, 7.7087655, 851 | 9.545092, 10.470132, 8.751504, 6.2894635, 852 | ], 853 | ) 854 | .unwrap(); 855 | let current_output17: Array3 = conv_layer17.transposed_convolve(&test_input17); 856 | 857 | assert!( 858 | arr_allclose(¤t_output17, &target_output17), 859 | "{:?} was not equal to {:?}", 860 | current_output17, 861 | target_output17 862 | ); 863 | 864 | let test_input18 = Array::from_shape_vec( 865 | (2, 6, 6), 866 | vec![ 867 | 0.01646563, 0.27730468, 0.62646276, 0.94121945, 0.66457146, 0.8425775, 0.4301739, 868 | 0.68211764, 0.6059688, 0.7629583, 0.56038123, 0.48163053, 0.437191, 0.43973154, 869 | 0.29997745, 0.21913478, 0.6611514, 0.209788, 0.176356, 0.88925517, 0.14793599, 870 | 0.36509594, 0.5616029, 0.6617385, 0.30053073, 0.36820948, 0.62227124, 0.4957459, 871 | 0.69923055, 0.3227648, 0.9702412, 0.5679234, 0.13432135, 0.7430913, 0.19231908, 872 | 0.26170298, 0.10480803, 0.00958306, 0.20726651, 0.20851518, 0.17973869, 0.23704103, 873 | 0.1163867, 0.2735905, 0.8537909, 0.40303, 0.75797635, 0.34342998, 0.74670875, 874 | 0.82946825, 0.91931367, 0.9654975, 0.18782942, 0.10021565, 0.05109123, 0.3069365, 875 | 0.10895161, 0.9891213, 0.62504834, 0.507994, 0.47125, 0.84314245, 0.6054993, 876 | 0.92367506, 0.6953895, 0.37023902, 0.67128026, 0.43450755, 0.28869846, 0.27641538, 877 | 0.8369712, 0.6856483, 878 | ], 879 | ) 880 | .unwrap(); 881 | 882 | let kernel18: Array4 = Array::from_shape_vec( 883 | (2, 1, 5, 5), 884 | vec![ 885 | 0.26890948, 0.65425915, 0.84581685, 0.51146317, 0.48995435, 0.52071315, 0.80564773, 886 | 0.670521, 0.3835857, 0.47078377, 0.5100531, 0.82525116, 0.02126783, 0.9583153, 887 | 0.23316967, 0.9487504, 0.9609589, 0.95734006, 0.00943314, 0.7521432, 0.46106157, 888 | 0.84370506, 0.16933449, 0.71423984, 0.5316646, 0.6400151, 0.9638914, 0.69432026, 889 | 0.8581641, 0.70813745, 0.2836983, 0.33953485, 0.8950543, 0.20262088, 0.81930757, 890 | 0.09556881, 0.22266188, 0.63842744, 0.8304186, 0.12425002, 0.7582065, 0.09737789, 891 | 0.84872884, 0.17490041, 0.37569723, 0.6669188, 0.8970669, 0.22398192, 0.29424527, 892 | 0.56277037, 893 | ], 894 | ) 895 | .unwrap(); 896 | let conv_layer18 = TransposedConvolutionLayer::new( 897 | kernel18, 898 | Some(Array::from_shape_vec((1,), vec![0.41113177]).unwrap()), 899 | 1, 900 | Padding::Same, 901 | ); 902 | let target_output18: Array3 = Array::from_shape_vec( 903 | (1, 6, 6), 904 | vec![ 905 | 5.2860155, 7.3411508, 9.014214, 8.658599, 8.115117, 4.8375845, 5.81894, 8.522849, 906 | 11.430818, 12.26867, 9.915437, 7.8984623, 8.078898, 11.37521, 14.505981, 15.461487, 907 | 12.289593, 9.001145, 9.641521, 11.294623, 14.67268, 14.926493, 11.998367, 8.302479, 908 | 7.0209365, 9.562044, 10.572207, 11.417736, 8.264289, 6.811918, 4.902732, 7.1602592, 909 | 8.855072, 7.738021, 6.735509, 4.974356, 910 | ], 911 | ) 912 | .unwrap(); 913 | let current_output18: Array3 = conv_layer18.transposed_convolve(&test_input18); 914 | 915 | assert!( 916 | arr_allclose(¤t_output18, &target_output18), 917 | "{:?} was not equal to {:?}", 918 | current_output18, 919 | target_output18 920 | ); 921 | 922 | let test_input19 = Array::from_shape_vec( 923 | (1, 4, 5), 924 | vec![ 925 | 0.7882464, 0.24159203, 0.5460915, 0.29690132, 0.3142075, 0.7648111, 0.04421633, 926 | 0.9636684, 0.98529965, 0.26998708, 0.13811645, 0.9724998, 0.41856614, 0.13321315, 927 | 0.0937838, 0.4742414, 0.79873264, 0.1621314, 0.5493846, 0.6481355, 928 | ], 929 | ) 930 | .unwrap(); 931 | 932 | let kernel19: Array4 = Array::from_shape_vec( 933 | (1, 2, 3, 3), 934 | vec![ 935 | 0.05176858, 0.17029643, 0.955536, 0.089831, 0.05664136, 0.9231865, 0.98567015, 936 | 0.01680515, 0.54690886, 0.8593988, 0.6496963, 0.8382851, 0.2400014, 0.36792266, 937 | 0.01756373, 0.6596718, 0.5680892, 0.19350345, 938 | ], 939 | ) 940 | .unwrap(); 941 | let conv_layer19 = TransposedConvolutionLayer::new( 942 | kernel19, 943 | Some(Array::from_shape_vec((2,), vec![0.4854058, 0.7869872]).unwrap()), 944 | 1, 945 | Padding::Same, 946 | ); 947 | let target_output19: Array3 = Array::from_shape_vec( 948 | (2, 4, 5), 949 | vec![ 950 | 0.6842892, 2.0640664, 1.0234097, 2.1371822, 1.7647654, 0.8579401, 2.573223, 2.1107073, 951 | 2.495966, 1.7212328, 0.75913584, 2.6720505, 3.2497058, 1.9795393, 1.7924366, 1.5449035, 952 | 1.487473, 1.9515271, 1.0480199, 1.1037331, 1.6698778, 2.518814, 2.5733294, 2.6612267, 953 | 1.9091775, 2.6116552, 2.8053112, 3.1332836, 2.2308586, 1.3121779, 2.5293965, 3.11231, 954 | 3.4430614, 2.8400254, 2.0495, 1.8731614, 1.9834092, 1.5063627, 1.3660567, 1.1141549, 955 | ], 956 | ) 957 | .unwrap(); 958 | let current_output19: Array3 = conv_layer19.transposed_convolve(&test_input19); 959 | 960 | assert!( 961 | arr_allclose(¤t_output19, &target_output19), 962 | "{:?} was not equal to {:?}", 963 | current_output19, 964 | target_output19 965 | ); 966 | 967 | let test_input20 = Array::from_shape_vec( 968 | (1, 4, 5), 969 | vec![ 970 | 0.12335967, 0.14544043, 0.25711668, 0.05608724, 0.18204254, 0.9562488, 0.44598028, 971 | 0.3229758, 0.8930679, 0.24216229, 0.21437034, 0.38366762, 0.64894366, 0.8650639, 972 | 0.7790032, 0.3529938, 0.37360683, 0.26240814, 0.554433, 0.90017563, 973 | ], 974 | ) 975 | .unwrap(); 976 | 977 | let kernel20: Array4 = Array::from_shape_vec( 978 | (1, 2, 3, 3), 979 | vec![ 980 | 0.6817857, 0.95833224, 0.28551313, 0.85106283, 0.70093375, 0.4791875, 0.5805011, 981 | 0.9803199, 0.97033703, 0.48236644, 0.66855663, 0.35855147, 0.25242767, 0.6717967, 982 | 0.26290378, 0.53677225, 0.18097793, 0.07985484, 983 | ], 984 | ) 985 | .unwrap(); 986 | let conv_layer20 = TransposedConvolutionLayer::new( 987 | kernel20, 988 | Some(Array::from_shape_vec((2,), vec![0.79416144, 0.7215235]).unwrap()), 989 | 1, 990 | Padding::Same, 991 | ); 992 | let target_output20: Array3 = Array::from_shape_vec( 993 | (2, 4, 5), 994 | vec![ 995 | 2.2248745, 2.0946596, 2.1375425, 2.224784, 1.4356921, 2.5163631, 3.1227217, 3.7412868, 996 | 3.736567, 2.6182625, 3.0602732, 3.9084072, 4.1730466, 4.9239025, 3.8796577, 1.7924206, 997 | 2.409352, 3.139607, 4.004576, 3.293878, 1.6955417, 1.7133851, 1.753269, 1.7024331, 998 | 1.3406746, 1.9052869, 2.1746416, 2.3581328, 2.7827106, 1.9874004, 1.7910466, 2.0327988, 999 | 2.6269865, 2.8862963, 2.3880367, 1.2977107, 1.5664427, 1.7484097, 2.0167332, 1.682083, 1000 | ], 1001 | ) 1002 | .unwrap(); 1003 | let current_output20: Array3 = conv_layer20.transposed_convolve(&test_input20); 1004 | 1005 | assert!( 1006 | arr_allclose(¤t_output20, &target_output20), 1007 | "{:?} was not equal to {:?}", 1008 | current_output20, 1009 | target_output20 1010 | ); 1011 | 1012 | let test_input21 = Array::from_shape_vec( 1013 | (1, 4, 5), 1014 | vec![ 1015 | 0.42313012, 0.6529702, 0.00384528, 0.08442257, 0.7838985, 0.980683, 0.36212954, 1016 | 0.61172485, 0.5263662, 0.8496614, 0.13177402, 0.9175598, 0.04323238, 0.03087392, 1017 | 0.03482862, 0.36226457, 0.6059544, 0.29194582, 0.34645593, 0.9409913, 1018 | ], 1019 | ) 1020 | .unwrap(); 1021 | 1022 | let kernel21: Array4 = Array::from_shape_vec( 1023 | (1, 2, 3, 3), 1024 | vec![ 1025 | 0.60953087, 0.8610255, 0.1627552, 0.8939359, 0.66327935, 0.48094535, 0.06551108, 1026 | 0.86129206, 0.65446067, 0.9592776, 0.8000413, 0.21450381, 0.6442533, 0.46971878, 1027 | 0.84573215, 0.34651902, 0.18830031, 0.57119006, 1028 | ], 1029 | ) 1030 | .unwrap(); 1031 | let conv_layer21 = TransposedConvolutionLayer::new( 1032 | kernel21, 1033 | Some(Array::from_shape_vec((2,), vec![0.27269888, 0.7915957]).unwrap()), 1034 | 1, 1035 | Padding::Same, 1036 | ); 1037 | let target_output21: Array3 = Array::from_shape_vec( 1038 | (2, 4, 5), 1039 | vec![ 1040 | 2.202188, 1.7570196, 1.5712463, 2.10197, 1.6504941, 2.3268435, 3.2088025, 1.9647117, 1041 | 1.8570077, 1.854845, 2.7299864, 2.7357655, 2.1297963, 2.1738572, 2.2535462, 1.2282722, 1042 | 1.9891862, 1.707246, 1.5412587, 1.1136687, 2.5429957, 2.545533, 2.472044, 2.706925, 1043 | 2.023878, 2.7771053, 3.354994, 2.3882847, 2.4607058, 1.866179, 2.625886, 3.0447614, 1044 | 2.8081274, 2.8505235, 2.121861, 1.6949112, 1.8337154, 2.2073488, 1.8500535, 1.550799, 1045 | ], 1046 | ) 1047 | .unwrap(); 1048 | let current_output21: Array3 = conv_layer21.transposed_convolve(&test_input21); 1049 | 1050 | assert!( 1051 | arr_allclose(¤t_output21, &target_output21), 1052 | "{:?} was not equal to {:?}", 1053 | current_output21, 1054 | target_output21 1055 | ); 1056 | 1057 | let test_input22 = Array::from_shape_vec( 1058 | (1, 4, 5), 1059 | vec![ 1060 | 0.21737069, 0.04463017, 0.393406, 0.8441482, 0.77705365, 0.6315176, 0.8522896, 1061 | 0.16809769, 0.22305161, 0.773223, 0.9678148, 0.2237789, 0.8982615, 0.271913, 1062 | 0.34780893, 0.49277794, 0.14560463, 0.33638617, 0.1585067, 0.18610527, 1063 | ], 1064 | ) 1065 | .unwrap(); 1066 | 1067 | let kernel22: Array4 = Array::from_shape_vec( 1068 | (1, 2, 5, 5), 1069 | vec![ 1070 | 0.6963084, 0.9429821, 0.40789625, 0.20056698, 0.37306353, 0.9367061, 0.7103306, 1071 | 0.06833509, 0.48335183, 0.45182207, 0.96346164, 0.00378525, 0.33689302, 0.28288215, 1072 | 0.75004077, 0.1607512, 0.514175, 0.35879079, 0.36688787, 0.00774159, 0.15532039, 1073 | 0.01133456, 0.94680935, 0.052598, 0.73530346, 0.8096563, 0.7097644, 0.37942106, 1074 | 0.38656324, 0.13763146, 0.99930644, 0.67476803, 0.06055127, 0.8463604, 0.4460729, 1075 | 0.33209085, 0.94373864, 0.5397614, 0.05766413, 0.64256257, 0.7357459, 0.09256811, 1076 | 0.8832945, 0.03911254, 0.782554, 0.4004812, 0.80732703, 0.8010379, 0.28867772, 1077 | 0.7802926, 1078 | ], 1079 | ) 1080 | .unwrap(); 1081 | let conv_layer22 = TransposedConvolutionLayer::new( 1082 | kernel22, 1083 | Some(Array::from_shape_vec((2,), vec![0.9490529, 0.5395702]).unwrap()), 1084 | 1, 1085 | Padding::Same, 1086 | ); 1087 | let target_output22: Array3 = Array::from_shape_vec( 1088 | (2, 4, 5), 1089 | vec![ 1090 | 3.438759, 3.8539677, 4.8715343, 3.1144986, 2.512818, 3.1302004, 4.025762, 4.9394965, 1091 | 3.7238846, 2.7845247, 3.5528362, 3.048192, 4.261737, 3.2427645, 3.4881544, 2.68011, 1092 | 3.1091578, 3.0516484, 2.670697, 2.4078283, 2.864305, 3.4669614, 4.9204173, 3.5656805, 1093 | 1.9317509, 3.8960943, 4.2974906, 4.443194, 4.268438, 2.9351158, 3.163786, 4.1768193, 1094 | 5.443554, 4.089055, 3.6120095, 3.8524146, 2.6254616, 4.461382, 2.9128237, 2.7010393, 1095 | ], 1096 | ) 1097 | .unwrap(); 1098 | let current_output22: Array3 = conv_layer22.transposed_convolve(&test_input22); 1099 | 1100 | assert!( 1101 | arr_allclose(¤t_output22, &target_output22), 1102 | "{:?} was not equal to {:?}", 1103 | current_output22, 1104 | target_output22 1105 | ); 1106 | 1107 | let test_input23 = Array::from_shape_vec( 1108 | (1, 4, 5), 1109 | vec![ 1110 | 0.1369102, 0.4513485, 0.1162144, 0.9589836, 0.24819064, 0.3149064, 0.22322156, 1111 | 0.39812055, 0.45308354, 0.5711523, 0.97817534, 0.06805564, 0.5386646, 0.6699801, 1112 | 0.1879456, 0.8407316, 0.64728755, 0.53731096, 0.63207716, 0.00916763, 1113 | ], 1114 | ) 1115 | .unwrap(); 1116 | 1117 | let kernel23: Array4 = Array::from_shape_vec( 1118 | (1, 2, 5, 5), 1119 | vec![ 1120 | 0.46754488, 0.8670982, 0.1932068, 0.43971533, 0.10441232, 0.46486124, 0.22910197, 1121 | 0.7679019, 0.23872072, 0.03931138, 0.8551502, 0.19610135, 0.8555064, 0.08035298, 1122 | 0.05671094, 0.9091975, 0.27582648, 0.33816597, 0.42012835, 0.7745094, 0.14011143, 1123 | 0.47505257, 0.98297566, 0.5085966, 0.22395916, 0.01153891, 0.669607, 0.12918296, 1124 | 0.55476546, 0.528783, 0.5067741, 0.14234911, 0.5152856, 0.6446142, 0.9118427, 1125 | 0.8193508, 0.989135, 0.7842302, 0.59264964, 0.35247177, 0.48483032, 0.22649786, 1126 | 0.04158387, 0.5814206, 0.8653732, 0.4524277, 0.44947338, 0.5519129, 0.79122764, 1127 | 0.7513198, 1128 | ], 1129 | ) 1130 | .unwrap(); 1131 | let conv_layer23 = TransposedConvolutionLayer::new( 1132 | kernel23, 1133 | Some(Array::from_shape_vec((2,), vec![0.9315116, 0.03313805]).unwrap()), 1134 | 1, 1135 | Padding::Same, 1136 | ); 1137 | let target_output23: Array3 = Array::from_shape_vec( 1138 | (2, 4, 5), 1139 | vec![ 1140 | 2.2144094, 3.9435155, 3.120901, 2.9545007, 2.1770444, 3.8536496, 4.704217, 4.4480824, 1141 | 3.3896554, 2.7172, 4.181309, 4.293991, 4.4760027, 4.101526, 2.8053095, 3.5478563, 1142 | 4.0226016, 3.8173618, 3.080534, 2.6638787, 1.2561212, 2.893108, 3.6574447, 2.5808244, 1143 | 2.467272, 2.3263056, 3.7484205, 4.928465, 3.3316998, 3.214223, 2.6942627, 4.366033, 1144 | 5.078573, 3.996016, 3.284345, 2.5444717, 3.8630738, 4.0135064, 2.516497, 2.4406507, 1145 | ], 1146 | ) 1147 | .unwrap(); 1148 | let current_output23: Array3 = conv_layer23.transposed_convolve(&test_input23); 1149 | 1150 | assert!( 1151 | arr_allclose(¤t_output23, &target_output23), 1152 | "{:?} was not equal to {:?}", 1153 | current_output23, 1154 | target_output23 1155 | ); 1156 | 1157 | let test_input24 = Array::from_shape_vec( 1158 | (1, 4, 5), 1159 | vec![ 1160 | 0.41213843, 0.45310238, 0.01799934, 0.4328227, 0.23842481, 0.20688592, 0.57869387, 1161 | 0.9324833, 0.43915927, 0.53123087, 0.5324186, 0.4149537, 0.22217223, 0.0080786, 1162 | 0.65063345, 0.7681623, 0.8421627, 0.27179125, 0.68530315, 0.9955617, 1163 | ], 1164 | ) 1165 | .unwrap(); 1166 | 1167 | let kernel24: Array4 = Array::from_shape_vec( 1168 | (1, 2, 5, 5), 1169 | vec![ 1170 | 0.77906156, 0.18006648, 0.5975193, 0.6555531, 0.9498266, 0.9690994, 0.5122471, 1171 | 0.3840358, 0.8757204, 0.7260429, 0.04691354, 0.40678895, 0.35535267, 0.19270384, 1172 | 0.23519029, 0.6665529, 0.26402712, 0.55168146, 0.08576613, 0.9567203, 0.57141143, 1173 | 0.20602623, 0.75538653, 0.96776, 0.44755465, 0.82362133, 0.29203537, 0.30861592, 1174 | 0.39762703, 0.26186082, 0.9658112, 0.27424002, 0.50570756, 0.8114843, 0.5536027, 1175 | 0.59788686, 0.05054026, 0.28394443, 0.6402348, 0.02258505, 0.60607, 0.6834324, 1176 | 0.8004999, 0.3349493, 0.4295082, 0.37033585, 0.0394791, 0.50698835, 0.29841292, 1177 | 0.9388161, 1178 | ], 1179 | ) 1180 | .unwrap(); 1181 | let conv_layer24 = TransposedConvolutionLayer::new( 1182 | kernel24, 1183 | Some(Array::from_shape_vec((2,), vec![0.84357476, 0.29760668]).unwrap()), 1184 | 1, 1185 | Padding::Same, 1186 | ); 1187 | let target_output24: Array3 = Array::from_shape_vec( 1188 | (2, 4, 5), 1189 | vec![ 1190 | 3.0206811, 3.061569, 4.395152, 3.5436916, 2.886642, 3.0099554, 4.4044333, 6.0680075, 1191 | 4.667347, 3.2426863, 3.5052178, 4.851921, 5.970953, 4.486573, 4.138274, 2.8315873, 1192 | 2.8526192, 4.4629803, 3.9374173, 3.2091482, 2.0807002, 2.5015216, 3.366962, 2.2886474, 1193 | 2.0462508, 2.8980908, 3.599226, 5.075294, 3.192652, 2.2037294, 2.8442726, 4.5077076, 1194 | 5.711252, 3.7258573, 2.942373, 2.0382986, 2.6738265, 3.560293, 2.5044878, 2.919956, 1195 | ], 1196 | ) 1197 | .unwrap(); 1198 | let current_output24: Array3 = conv_layer24.transposed_convolve(&test_input24); 1199 | 1200 | assert!( 1201 | arr_allclose(¤t_output24, &target_output24), 1202 | "{:?} was not equal to {:?}", 1203 | current_output24, 1204 | target_output24 1205 | ); 1206 | 1207 | let test_input25 = Array::from_shape_vec( 1208 | (1, 3, 3), 1209 | vec![ 1210 | 0.25654384, 0.71037215, 0.14646496, 0.9906275, 0.9552909, 0.7966291, 0.6094893, 1211 | 0.7271988, 0.09518696, 1212 | ], 1213 | ) 1214 | .unwrap(); 1215 | 1216 | let kernel25: Array4 = Array::from_shape_vec( 1217 | (1, 2, 3, 3), 1218 | vec![ 1219 | 0.3957746, 0.6157527, 0.31079137, 0.46793708, 0.8514491, 0.86081386, 0.8208274, 1220 | 0.2372746, 0.6977758, 0.79387194, 0.9463199, 0.99063903, 0.29514915, 0.8795419, 1221 | 0.741553, 0.03520088, 0.83411527, 0.47134155, 1222 | ], 1223 | ) 1224 | .unwrap(); 1225 | let conv_layer25 = TransposedConvolutionLayer::new( 1226 | kernel25, 1227 | Some(Array::from_shape_vec((2,), vec![0.26704508, 0.2872401]).unwrap()), 1228 | 1, 1229 | Padding::Same, 1230 | ); 1231 | let target_output25: Array3 = Array::from_shape_vec( 1232 | (2, 3, 3), 1233 | vec![ 1234 | 1.8059499, 2.3726506, 1.7906733, 2.8645957, 3.448602, 2.5827134, 2.1454573, 3.027213, 1235 | 1.8296733, 2.418376, 3.6632986, 2.643055, 2.8335593, 4.183306, 2.9637728, 1.8978682, 1236 | 2.6986935, 2.0249662, 1237 | ], 1238 | ) 1239 | .unwrap(); 1240 | let current_output25: Array3 = conv_layer25.transposed_convolve(&test_input25); 1241 | 1242 | assert!( 1243 | arr_allclose(¤t_output25, &target_output25), 1244 | "{:?} was not equal to {:?}", 1245 | current_output25, 1246 | target_output25 1247 | ); 1248 | 1249 | let test_input26 = Array::from_shape_vec( 1250 | (1, 3, 3), 1251 | vec![ 1252 | 0.04289522, 0.583651, 0.94414073, 0.5729697, 0.72244126, 0.7707775, 0.02079507, 1253 | 0.22973806, 0.18128674, 1254 | ], 1255 | ) 1256 | .unwrap(); 1257 | 1258 | let kernel26: Array4 = Array::from_shape_vec( 1259 | (1, 2, 3, 3), 1260 | vec![ 1261 | 0.05054512, 0.6523751, 0.27093726, 0.32479268, 0.53774405, 0.08921466, 0.18494032, 1262 | 0.78120434, 0.15868789, 0.81183356, 0.21792394, 0.6620494, 0.6886836, 0.39724785, 1263 | 0.59319293, 0.02472531, 0.21891202, 0.5342134, 1264 | ], 1265 | ) 1266 | .unwrap(); 1267 | let conv_layer26 = TransposedConvolutionLayer::new( 1268 | kernel26, 1269 | Some(Array::from_shape_vec((2,), vec![0.4041879, 0.53426075]).unwrap()), 1270 | 1, 1271 | Padding::Same, 1272 | ); 1273 | let target_output26: Array3 = Array::from_shape_vec( 1274 | (2, 3, 3), 1275 | vec![ 1276 | 1.0271271, 1.6940202, 1.6625365, 1.1135714, 1.8961767, 1.893818, 1.0712025, 1.3863095, 1277 | 1.2389472, 1.6646175, 2.604289, 1.901799, 1.4742672, 2.0769873, 1.9790801, 0.8440309, 1278 | 1.2460053, 1.2972257, 1279 | ], 1280 | ) 1281 | .unwrap(); 1282 | let current_output26: Array3 = conv_layer26.transposed_convolve(&test_input26); 1283 | 1284 | assert!( 1285 | arr_allclose(¤t_output26, &target_output26), 1286 | "{:?} was not equal to {:?}", 1287 | current_output26, 1288 | target_output26 1289 | ); 1290 | 1291 | let test_input27 = Array::from_shape_vec( 1292 | (1, 3, 3), 1293 | vec![ 1294 | 0.20461221, 0.29362342, 0.7835011, 0.47930118, 0.2895624, 0.7320774, 0.55179524, 1295 | 0.37774232, 0.04187773, 1296 | ], 1297 | ) 1298 | .unwrap(); 1299 | 1300 | let kernel27: Array4 = Array::from_shape_vec( 1301 | (1, 2, 3, 3), 1302 | vec![ 1303 | 0.23883641, 0.63051957, 0.97746944, 0.6415133, 0.4162999, 0.04763152, 0.75958186, 1304 | 0.13098118, 0.6238241, 0.4102024, 0.5710282, 0.59153587, 0.66274744, 0.6454271, 1305 | 0.8120847, 0.22876394, 0.6206928, 0.19057496, 1306 | ], 1307 | ) 1308 | .unwrap(); 1309 | let conv_layer27 = TransposedConvolutionLayer::new( 1310 | kernel27, 1311 | Some(Array::from_shape_vec((2,), vec![0.4622738, 0.9772445]).unwrap()), 1312 | 1, 1313 | Padding::Same, 1314 | ); 1315 | let target_output27: Array3 = Array::from_shape_vec( 1316 | (2, 3, 3), 1317 | vec![ 1318 | 1.107184, 1.9228053, 1.5470586, 1.5355327, 2.624059, 1.4622594, 1.2170386, 1.5656755, 1319 | 0.7742243, 1.6963787, 2.6013548, 2.3107078, 2.1427183, 2.9983187, 2.4745288, 1.947476, 1320 | 2.1354535, 1.8206108, 1321 | ], 1322 | ) 1323 | .unwrap(); 1324 | let current_output27: Array3 = conv_layer27.transposed_convolve(&test_input27); 1325 | 1326 | assert!( 1327 | arr_allclose(¤t_output27, &target_output27), 1328 | "{:?} was not equal to {:?}", 1329 | current_output27, 1330 | target_output27 1331 | ); 1332 | 1333 | let test_input28 = Array::from_shape_vec( 1334 | (1, 3, 3), 1335 | vec![ 1336 | 0.17967562, 0.27354005, 0.48198408, 0.09082693, 0.6217685, 0.0965838, 0.8904992, 1337 | 0.5337058, 0.17222154, 1338 | ], 1339 | ) 1340 | .unwrap(); 1341 | 1342 | let kernel28: Array4 = Array::from_shape_vec( 1343 | (1, 2, 5, 5), 1344 | vec![ 1345 | 0.62424076, 0.95776683, 0.998361, 0.150883, 0.28713444, 0.35105017, 0.1765902, 1346 | 0.9674033, 0.65025777, 0.1959308, 0.3119046, 0.55247456, 0.38556734, 0.18145509, 1347 | 0.94255227, 0.7036003, 0.3402552, 0.16401337, 0.28418577, 0.5504918, 0.6148071, 1348 | 0.7740251, 0.37459207, 0.25677747, 0.80441076, 0.17314304, 0.5946227, 0.30094627, 1349 | 0.06480476, 0.5798754, 0.62549657, 0.6065069, 0.9259787, 0.3677915, 0.69532603, 1350 | 0.8356569, 0.39276573, 0.18570696, 0.61698794, 0.9064707, 0.01547038, 0.2551995, 1351 | 0.11438051, 0.03861368, 0.6524532, 0.36442748, 0.08230381, 0.79969245, 0.27816203, 1352 | 0.3741886, 1353 | ], 1354 | ) 1355 | .unwrap(); 1356 | let conv_layer28 = TransposedConvolutionLayer::new( 1357 | kernel28, 1358 | Some(Array::from_shape_vec((2,), vec![0.44356522, 0.27386904]).unwrap()), 1359 | 1, 1360 | Padding::Same, 1361 | ); 1362 | let target_output28: Array3 = Array::from_shape_vec( 1363 | (2, 3, 3), 1364 | vec![ 1365 | 2.5535824, 2.3576782, 1.8720919, 2.3300653, 2.1388383, 1.6230651, 2.0052605, 1.588402, 1366 | 2.0840113, 1.954235, 1.6132926, 1.6790831, 1.9694752, 1.5707091, 1.915636, 1.3051853, 1367 | 1.3977213, 2.0654297, 1368 | ], 1369 | ) 1370 | .unwrap(); 1371 | let current_output28: Array3 = conv_layer28.transposed_convolve(&test_input28); 1372 | 1373 | assert!( 1374 | arr_allclose(¤t_output28, &target_output28), 1375 | "{:?} was not equal to {:?}", 1376 | current_output28, 1377 | target_output28 1378 | ); 1379 | 1380 | let test_input29 = Array::from_shape_vec( 1381 | (1, 3, 3), 1382 | vec![ 1383 | 0.4171807, 0.14990133, 0.4750283, 0.6358263, 0.77572674, 0.7072159, 0.34310365, 1384 | 0.36999545, 0.8200852, 1385 | ], 1386 | ) 1387 | .unwrap(); 1388 | 1389 | let kernel29: Array4 = Array::from_shape_vec( 1390 | (1, 2, 5, 5), 1391 | vec![ 1392 | 0.46510443, 0.9230857, 0.86201537, 0.04691705, 0.12260284, 0.89921826, 0.47400048, 1393 | 0.7492544, 0.05440097, 0.28273952, 0.9184355, 0.9064988, 0.7027802, 0.19125043, 1394 | 0.8670149, 0.08634489, 0.722654, 0.77540857, 0.00211577, 0.6443405, 0.2344423, 1395 | 0.9849179, 0.9893316, 0.87634915, 0.48734385, 0.8760625, 0.53053117, 0.01152504, 1396 | 0.6788874, 0.76156837, 0.4680538, 0.07214947, 0.549209, 0.8383253, 0.00365476, 1397 | 0.9782156, 0.7419056, 0.40790308, 0.00302504, 0.80174476, 0.7689671, 0.50054824, 1398 | 0.16478458, 0.89107823, 0.7763043, 0.73042727, 0.41667554, 0.7348974, 0.71259016, 1399 | 0.93557405, 1400 | ], 1401 | ) 1402 | .unwrap(); 1403 | let conv_layer29 = TransposedConvolutionLayer::new( 1404 | kernel29, 1405 | Some(Array::from_shape_vec((2,), vec![0.37013116, 0.14954911]).unwrap()), 1406 | 1, 1407 | Padding::Same, 1408 | ); 1409 | let target_output29: Array3 = Array::from_shape_vec( 1410 | (2, 3, 3), 1411 | vec![ 1412 | 3.7342412, 3.0289524, 2.6125512, 3.8124187, 2.822994, 2.9358318, 3.4862623, 3.5348666, 1413 | 3.079057, 2.5504904, 2.2467468, 2.2412186, 2.7842636, 2.1768038, 2.2476804, 3.1191318, 1414 | 2.563703, 2.9078498, 1415 | ], 1416 | ) 1417 | .unwrap(); 1418 | let current_output29: Array3 = conv_layer29.transposed_convolve(&test_input29); 1419 | 1420 | assert!( 1421 | arr_allclose(¤t_output29, &target_output29), 1422 | "{:?} was not equal to {:?}", 1423 | current_output29, 1424 | target_output29 1425 | ); 1426 | 1427 | let test_input30 = Array::from_shape_vec( 1428 | (1, 3, 3), 1429 | vec![ 1430 | 0.33766475, 0.39673004, 0.03940821, 0.2414454, 0.9373305, 0.84819096, 0.9272432, 1431 | 0.8604789, 0.92845494, 1432 | ], 1433 | ) 1434 | .unwrap(); 1435 | 1436 | let kernel30: Array4 = Array::from_shape_vec( 1437 | (1, 2, 5, 5), 1438 | vec![ 1439 | 0.8023847, 0.06647868, 0.12827316, 0.02774826, 0.8982795, 0.1193029, 0.37523487, 1440 | 0.28379655, 0.0638791, 0.08446726, 0.68760884, 0.2630593, 0.01052708, 0.45302406, 1441 | 0.10402057, 0.5539082, 0.39986408, 0.53770447, 0.24856173, 0.12796782, 0.80071914, 1442 | 0.38390577, 0.6908975, 0.5850918, 0.71101063, 0.17578217, 0.55867785, 0.71616316, 1443 | 0.3247584, 0.9196999, 0.46712968, 0.08430128, 0.83232373, 0.49657014, 0.24024557, 1444 | 0.60874844, 0.47418702, 0.9770886, 0.99522823, 0.7749884, 0.7253688, 0.26021662, 1445 | 0.8869578, 0.5181486, 0.5871326, 0.76900613, 0.5745777, 0.81429285, 0.78002185, 1446 | 0.7069771, 1447 | ], 1448 | ) 1449 | .unwrap(); 1450 | let conv_layer30 = TransposedConvolutionLayer::new( 1451 | kernel30, 1452 | Some(Array::from_shape_vec((2,), vec![0.33134556, 0.7407027]).unwrap()), 1453 | 1, 1454 | Padding::Same, 1455 | ); 1456 | let target_output30: Array3 = Array::from_shape_vec( 1457 | (2, 3, 3), 1458 | vec![ 1459 | 1.9089153, 1.2963923, 1.8434926, 2.2225122, 1.638551, 1.3498158, 2.5974822, 2.394682, 1460 | 2.046781, 3.2669327, 3.8907242, 4.462316, 3.6467514, 4.0910583, 4.5510373, 4.2264524, 1461 | 4.7308207, 5.1828766, 1462 | ], 1463 | ) 1464 | .unwrap(); 1465 | let current_output30: Array3 = conv_layer30.transposed_convolve(&test_input30); 1466 | 1467 | assert!( 1468 | arr_allclose(¤t_output30, &target_output30), 1469 | "{:?} was not equal to {:?}", 1470 | current_output30, 1471 | target_output30 1472 | ); 1473 | } 1474 | -------------------------------------------------------------------------------- /tests/npy_files/input_rand_same_shape.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/input_rand_same_shape.npy -------------------------------------------------------------------------------- /tests/npy_files/input_rand_same_shapes.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/input_rand_same_shapes.npy -------------------------------------------------------------------------------- /tests/npy_files/kernel.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/kernel.npy -------------------------------------------------------------------------------- /tests/npy_files/kernel_rand_same_shape.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/kernel_rand_same_shape.npy -------------------------------------------------------------------------------- /tests/npy_files/out1.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/out1.npy -------------------------------------------------------------------------------- /tests/npy_files/output.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/output.npy -------------------------------------------------------------------------------- /tests/npy_files/output_rand_same_shape.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/output_rand_same_shape.npy -------------------------------------------------------------------------------- /tests/npy_files/output_simple_example.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/output_simple_example.npy -------------------------------------------------------------------------------- /tests/npy_files/simple_example_input.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/simple_example_input.npy -------------------------------------------------------------------------------- /tests/npy_files/simple_weight.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/simple_weight.npy -------------------------------------------------------------------------------- /tests/npy_files/weight1.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/weight1.npy -------------------------------------------------------------------------------- /tests/npy_files/x1.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/x1.npy -------------------------------------------------------------------------------- /tests/npy_files/y_hat.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conzel/convolutions-rs/5c0577daf27a90139853437674a8de3457eaccf0/tests/npy_files/y_hat.npy --------------------------------------------------------------------------------