[lua-torch-nn] 01/07: New upstream version 0~20160908-g9d7b9ea+dfsg

Zhou Mo cdluminate-guest at moszumanska.debian.org
Sat Sep 10 03:27:37 UTC 2016


This is an automated email from the git hooks/post-receive script.

cdluminate-guest pushed a commit to branch master
in repository lua-torch-nn.

commit 02e4a99fbdf67a54e0bd2360c96ffa01236f170e
Author: Zhou Mo <cdluminate at gmail.com>
Date:   Sat Sep 10 02:31:27 2016 +0000

    New upstream version 0~20160908-g9d7b9ea+dfsg
---
 BCECriterion.lua                                   | 142 +++-----
 CMaxTable.lua                                      |  33 ++
 CMinTable.lua                                      |  33 ++
 CMul.lua                                           |  61 +++-
 CONTRIBUTING.md                                    |   2 +-
 ClassSimplexCriterion.lua                          |   2 +-
 Concat.lua                                         |  10 +-
 Container.lua                                      |   2 +-
 DepthConcat.lua                                    |  18 +-
 Dropout.lua                                        |   5 +-
 JoinTable.lua                                      |   2 +-
 LookupTable.lua                                    |   2 +-
 MapTable.lua                                       |  99 ++++++
 Max.lua                                            |  15 +-
 Min.lua                                            |  15 +-
 Normalize.lua                                      |  15 +-
 SpatialConvolution.lua                             |  23 +-
 SpatialDilatedMaxPooling.lua                       |   4 +-
 SpatialDropout.lua                                 |   7 +-
 SpatialMaxPooling.lua                              |   2 -
 Sum.lua                                            |   4 +-
 THNN.lua                                           |   1 +
 TemporalDynamicKMaxPooling.lua                     |  65 ++++
 Threshold.lua                                      |   1 +
 VolumetricDilatedMaxPooling.lua                    |  64 ++++
 VolumetricDropout.lua                              |   7 +-
 doc/convolution.md                                 |  25 ++
 doc/simple.md                                      |  72 ++--
 doc/table.md                                       |  81 +++++
 init.lua                                           |   5 +
 lib/THNN/generic/BCECriterion.c                    |  50 +++
 lib/THNN/generic/HardTanh.c                        |   2 +-
 lib/THNN/generic/SpatialConvolutionMM.c            |  47 ++-
 ...tialMaxPooling.c => SpatialDilatedMaxPooling.c} |  18 +-
 lib/THNN/generic/SpatialMaxPooling.c               | 288 +---------------
 lib/THNN/generic/SpatialUpSamplingNearest.c        |   4 +-
 lib/THNN/generic/THNN.h                            |  58 +++-
 lib/THNN/generic/Threshold.c                       |   1 +
 ...cMaxPooling.c => VolumetricDilatedMaxPooling.c} |  87 +++--
 lib/THNN/generic/VolumetricMaxPooling.c            | 361 +--------------------
 lib/THNN/init.c                                    |   9 +
 test.lua                                           | 232 ++++++++++++-
 42 files changed, 1067 insertions(+), 907 deletions(-)

diff --git a/BCECriterion.lua b/BCECriterion.lua
index b319335..8bb5f81 100644
--- a/BCECriterion.lua
+++ b/BCECriterion.lua
@@ -1,106 +1,64 @@
+local THNN = require 'nn.THNN'
 local BCECriterion, parent = torch.class('nn.BCECriterion', 'nn.Criterion')
 
-local eps = 1e-12
-
 function BCECriterion:__init(weights, sizeAverage)
-    parent.__init(self)
-    if sizeAverage ~= nil then
-        self.sizeAverage = sizeAverage
-    else
-        self.sizeAverage = true
-    end
-    if weights ~= nil then
-        assert(weights:dim() == 1, "weights input should be 1-D Tensor")
-        self.weights = weights
-    end
+   parent.__init(self)
+   if sizeAverage ~= nil then
+      self.sizeAverage = sizeAverage
+   else
+      self.sizeAverage = true
+   end
+   if weights ~= nil then
+      assert(weights:dim() == 1, "weights input should be 1-D Tensor")
+      self.weights = weights
+   end
 end
 
 
 function BCECriterion:__len()
-    if (self.weights) then
-        return #self.weights
-    else
-        return 0
-    end
+   return self.weights and #self.weights or 0
 end
 
 function BCECriterion:updateOutput(input, target)
-    -- - log(input) * target - log(1 - input) * (1 - target)
-
-    assert( input:nElement() == target:nElement(),
-    "input and target size mismatch")
-
-    self.buffer = self.buffer or input.new()
-
-    local buffer = self.buffer
-    local weights = self.weights
-    local output
-
-    buffer:resizeAs(input)
-
-    if weights ~= nil and target:dim() ~= 1 then
-        weights = self.weights:view(1, target:size(2)):expandAs(target)
-    end
-
-    -- log(input) * target
-    buffer:add(input, eps):log()
-    if weights ~= nil then buffer:cmul(weights) end
-
-    output = torch.dot(target, buffer)
-
-    -- log(1 - input) * (1 - target)
-    buffer:mul(input, -1):add(1):add(eps):log()
-    if weights ~= nil then buffer:cmul(weights) end
-
-    output = output + torch.sum(buffer)
-    output = output - torch.dot(target, buffer)
-
-    if self.sizeAverage then
-        output = output / input:nElement()
-    end
-
-    self.output = - output
-
-    return self.output
+   -- - log(input) * target - log(1 - input) * (1 - target)
+   assert( input:nElement() == target:nElement(),
+   "input and target size mismatch")
+   self.output_tensor = self.output_tensor or input.new(1)
+
+   local weights = self.weights
+   if weights ~= nil and target:dim() ~= 1 then
+      weights = self.weights:view(1, target:size(2)):expandAs(target)
+   end
+
+   input.THNN.BCECriterion_updateOutput(
+      input:cdata(),
+      target:cdata(),
+      self.output_tensor:cdata(),
+      self.sizeAverage,
+      THNN.optionalTensor(weights)
+   )
+
+   self.output = self.output_tensor[1]
+   return self.output
 end
 
 function BCECriterion:updateGradInput(input, target)
-    -- - (target - input) / ( input (1 - input) )
-    -- The gradient is slightly incorrect:
-    -- It should have be divided by (input + eps) (1 - input + eps)
-    -- but it is divided by input (1 - input + eps) + eps
-    -- This modification requires less memory to be computed.
-
-    assert( input:nElement() == target:nElement(),
-    "input and target size mismatch")
-
-    self.buffer = self.buffer or input.new()
-
-    local buffer = self.buffer
-    local weights = self.weights
-    local gradInput = self.gradInput
-
-    if weights ~= nil and target:dim() ~= 1 then
-        weights = self.weights:view(1, target:size(2)):expandAs(target)
-    end
-
-    buffer:resizeAs(input)
-    -- - x ( 1 + eps -x ) + eps
-    buffer:add(input, -1):add(-eps):cmul(input):add(-eps)
-
-    gradInput:resizeAs(input)
-    -- y - x
-    gradInput:add(target, -1, input)
-    -- - (y - x) / ( x ( 1 + eps -x ) + eps )
-    gradInput:cdiv(buffer)
-
-    if weights ~= nil then
-        gradInput:cmul(weights)
-    end
-
-    if self.sizeAverage then
-        gradInput:div(target:nElement())
-    end
-
-    return gradInput
+   -- - (target - input) / ( input (1 - input) )
+   assert( input:nElement() == target:nElement(),
+   "input and target size mismatch")
+
+   local weights = self.weights
+   if weights ~= nil and target:dim() ~= 1 then
+      weights = self.weights:view(1, target:size(2)):expandAs(target)
+   end
+
+   input.THNN.BCECriterion_updateGradInput(
+      input:cdata(),
+      target:cdata(),
+      self.gradInput:cdata(),
+      self.sizeAverage,
+      THNN.optionalTensor(weights)
+   )
+
+   return self.gradInput
 end
diff --git a/CMaxTable.lua b/CMaxTable.lua
new file mode 100644
index 0000000..3907faf
--- /dev/null
+++ b/CMaxTable.lua
@@ -0,0 +1,33 @@
+local CMaxTable, parent = torch.class('nn.CMaxTable', 'nn.Module')
+
+function CMaxTable:__init()
+   parent.__init(self)
+   self.gradInput = {}
+   self.maxIdx = torch.Tensor()
+end
+
+function CMaxTable:updateOutput(input)
+   self.output:resizeAs(input[1]):copy(input[1])
+   self.maxIdx:resizeAs(input[1]):fill(1)
+   for i=2,#input do
+      local mask = torch.gt(input[i], self.output)
+      self.maxIdx:maskedFill(mask, i)
+      self.output:maskedCopy(mask, input[i][mask])
+   end
+   return self.output
+end
+
+function CMaxTable:updateGradInput(input, gradOutput)
+   for i=1,#input do
+      self.gradInput[i] = torch.Tensor()
+      self.gradInput[i]:resizeAs(input[i]):fill(0.0)
+      local mask = torch.eq(self.maxIdx, i)
+      self.gradInput[i]:maskedCopy(mask, gradOutput[mask])
+   end
+
+   for i=#input+1, #self.gradInput do
+       self.gradInput[i] = nil
+   end
+
+   return self.gradInput
+end
diff --git a/CMinTable.lua b/CMinTable.lua
new file mode 100644
index 0000000..a8385e8
--- /dev/null
+++ b/CMinTable.lua
@@ -0,0 +1,33 @@
+local CMinTable, parent = torch.class('nn.CMinTable', 'nn.Module')
+
+function CMinTable:__init()
+   parent.__init(self)
+   self.gradInput = {}
+   self.minIdx = torch.Tensor()
+end
+
+function CMinTable:updateOutput(input)
+   self.output:resizeAs(input[1]):copy(input[1])
+   self.minIdx:resizeAs(input[1]):fill(1)
+   for i=2,#input do
+      local mask = torch.lt(input[i], self.output)
+      self.minIdx:maskedFill(mask, i)
+      self.output:maskedCopy(mask, input[i][mask])
+   end
+   return self.output
+end
+
+function CMinTable:updateGradInput(input, gradOutput)
+   for i=1,#input do
+      self.gradInput[i] = torch.Tensor()
+      self.gradInput[i]:resizeAs(input[i]):fill(0.0)
+      local mask = torch.eq(self.minIdx, i)
+      self.gradInput[i]:maskedCopy(mask, gradOutput[mask])
+   end
+
+   for i=#input+1, #self.gradInput do
+       self.gradInput[i] = nil
+   end
+
+   return self.gradInput
+end
diff --git a/CMul.lua b/CMul.lua
index e84f7ba..22b5b74 100644
--- a/CMul.lua
+++ b/CMul.lua
@@ -47,10 +47,15 @@ function CMul:updateOutput(input)
       
       self._output:cmul(self._weight)
    else
-      local batchSize = input:size(1)
-      self._output:view(self.output, batchSize, -1)
-      self._weight:view(self.weight, 1, -1)
-      
+      if self.weight:dim() == input:dim() then
+         self._output:set(self.output)
+         self._weight:set(self.weight)
+      else
+         local batchSize = input:size(1)
+         self._output:view(self.output, batchSize, -1)
+         self._weight:view(self.weight, 1, -1)
+      end
+
       self._expand:expandAs(self._weight, self._output)
       
       if torch.type(input) == 'torch.CudaTensor' then
@@ -76,10 +81,17 @@ function CMul:updateGradInput(input, gradOutput)
    if self.weight:nElement() == gradOutput:nElement() then
       self.gradInput:addcmul(1, self.weight, gradOutput)
    else
-      local batchSize = input:size(1)
-      nn.utils.contiguousView(self._gradOutput, gradOutput, batchSize, -1)
-      nn.utils.contiguousView(self._gradInput, self.gradInput, batchSize, -1)
-      self._weight:view(self.weight, 1, -1)
+      if self.weight:dim() == input:dim() then
+         nn.utils.contiguousView(self._gradOutput, gradOutput, gradOutput:size())
+         nn.utils.contiguousView(self._gradInput, self.gradInput, self.gradInput:size())
+         self._weight:set(self.weight)
+      else
+         local batchSize = input:size(1)
+         nn.utils.contiguousView(self._gradOutput, gradOutput, batchSize, -1)
+         nn.utils.contiguousView(self._gradInput, self.gradInput, batchSize, -1)
+         self._weight:view(self.weight, 1, -1)
+      end
+
       self._expand:expandAs(self._weight, self._gradOutput)
       
       if torch.type(input) == 'torch.CudaTensor' then
@@ -103,14 +115,33 @@ function CMul:accGradParameters(input, gradOutput, scale)
    if self.weight:nElement() == gradOutput:nElement() then
       self.gradWeight:addcmul(scale, input, gradOutput)
    else
-      local batchSize = input:size(1)
-      nn.utils.contiguousView(self._input, input, batchSize, -1)
-      nn.utils.contiguousView(self._gradOutput, gradOutput, batchSize, -1)
-      self._gradWeight:view(self.gradWeight, 1, -1)
+      if self.weight:dim() == input:dim() then
+         nn.utils.contiguousView(self._input, input, input:size())
+         nn.utils.contiguousView(self._gradOutput, gradOutput, gradOutput:size())
+         self._gradWeight:set(self.gradWeight)
       
-      self._repeat:cmul(self._input, self._gradOutput)
-      self._sum:sum(self._repeat, 1)
-      self._gradWeight:add(scale, self._sum)
+         self._repeat:cmul(self._input, self._gradOutput)
+         local sumInto = self._sum
+         local sumFrom = self._repeat
+         for i=1,self.weight:dim() do
+            if self.weight:size(i) ~= input:size(i) then
+               sumInto:sum(sumFrom, i)
+               sumInto = sumFrom
+               sumFrom = sumFrom == self._repeat and self._sum or self._repeat
+            end
+         end
+         self._gradWeight:add(scale, sumFrom)
+      else
+         local batchSize = input:size(1)
+         nn.utils.contiguousView(self._input, input, batchSize, -1)
+         nn.utils.contiguousView(self._gradOutput, gradOutput, batchSize, -1)
+         self._gradWeight:view(self.gradWeight, 1, -1)
+
+         self._repeat:cmul(self._input, self._gradOutput)
+         self._sum:sum(self._repeat, 1)
+         self._gradWeight:add(scale, self._sum)
+      end
+
    end
 end
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d4da7c9..92574db 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -22,7 +22,7 @@ restrictions:
   [mailing-list](http://groups.google.com/forum/#!forum/torch7)).
 
 * Please **do not** open issues regarding the code in a torch package 
-  outside the core. For example dont open issues about the 
+  outside the core. For example don't open issues about the 
   REPL in the nn issue tracker, use the trepl issue tracker for that.
 
 <a name="bugs"></a>
diff --git a/ClassSimplexCriterion.lua b/ClassSimplexCriterion.lua
index 6ccaed9..9cabc01 100644
--- a/ClassSimplexCriterion.lua
+++ b/ClassSimplexCriterion.lua
@@ -64,7 +64,7 @@ function ClassSimplexCriterion:__init(nClasses)
 end
 
 -- handle target being both 1D tensor, and
--- target being 2D tensor (2D tensor means dont do anything)
+-- target being 2D tensor (2D tensor means don't do anything)
 local function transformTarget(self, target)
     if torch.type(target) == 'number' then
         self._target:resize(self.nClasses)
diff --git a/Concat.lua b/Concat.lua
index ea2489e..108b216 100644
--- a/Concat.lua
+++ b/Concat.lua
@@ -2,22 +2,24 @@ local Concat, parent = torch.class('nn.Concat', 'nn.Container')
 
 function Concat:__init(dimension)
    parent.__init(self)
-   self.size = torch.LongStorage()
+   self.outputSize = torch.LongStorage()
    self.dimension = dimension
 end
 
 function Concat:updateOutput(input)
+   self.outputSize = self.outputSize or torch.LongStorage()
+
    local outs = {}
    for i=1,#self.modules do
       local currentOutput = self:rethrowErrors(self.modules[i], i, 'updateOutput', input)
       outs[i] = currentOutput
       if i == 1 then
-         self.size:resize(currentOutput:dim()):copy(currentOutput:size())
+         self.outputSize:resize(currentOutput:dim()):copy(currentOutput:size())
       else
-         self.size[self.dimension] = self.size[self.dimension] + currentOutput:size(self.dimension)
+         self.outputSize[self.dimension] = self.outputSize[self.dimension] + currentOutput:size(self.dimension)
       end
    end
-   self.output:resize(self.size)
+   self.output:resize(self.outputSize)
 
    local offset = 1
    for i,module in ipairs(self.modules) do
diff --git a/Container.lua b/Container.lua
index 6af4d7d..469a370 100644
--- a/Container.lua
+++ b/Container.lua
@@ -22,7 +22,7 @@ end
 
 -- Check if passing arguments through xpcall is supported in this Lua interpreter.
 local _, XPCALL_ARGS = xpcall(function(x) return x ~= nil end, function() end, 1)
-local TRACEBACK_WARNING = "WARNING: If you see a stack trace below, it doesn't point to the place where this error occured. Please use only the one above."
+local TRACEBACK_WARNING = "WARNING: If you see a stack trace below, it doesn't point to the place where this error occurred. Please use only the one above."
 -- module argument can be retrieved with moduleIndex, but code is cleaner when
 -- it has to be specified anyway.
 function Container:rethrowErrors(module, moduleIndex, funcName, ...)
diff --git a/DepthConcat.lua b/DepthConcat.lua
index 8ae8384..f64a90e 100644
--- a/DepthConcat.lua
+++ b/DepthConcat.lua
@@ -13,13 +13,13 @@ local DepthConcat, _ = torch.class('nn.DepthConcat', 'nn.Concat')
 
 function DepthConcat:windowNarrow(output, currentOutput, offset)
    local outputWindow = output:narrow(self.dimension, offset, currentOutput:size(self.dimension))
-   for dim=1,self.size:size(1) do
+   for dim=1,self.outputSize:size(1) do
       local currentSize = currentOutput:size(dim)
-      if dim ~= self.dimension and self.size[dim] ~= currentSize then
+      if dim ~= self.dimension and self.outputSize[dim] ~= currentSize then
          -- 5x5 vs 3x3 -> start = [(5-3)/2] + 1 = 2 (1 pad each side)
          -- 9x9 vs 5x5 -> start = [(9-5)/2] + 1 = 3 (2 pad each side)
          -- 9x9 vs 4x4 -> start = [(9-4)/2] + 1 = 3.5 (2 pad, 3 pad)
-         local start = math.floor(((self.size[dim] - currentSize) / 2) + 1)
+         local start = math.floor(((self.outputSize[dim] - currentSize) / 2) + 1)
          outputWindow = outputWindow:narrow(dim, start, currentSize)
       end
    end
@@ -27,23 +27,25 @@ function DepthConcat:windowNarrow(output, currentOutput, offset)
 end
 
 function DepthConcat:updateOutput(input)
+   self.outputSize = self.outputSize or torch.LongStorage()
+
    local outs = {}
    for i=1,#self.modules do
       local currentOutput = self:rethrowErrors(self.modules[i], i, 'updateOutput', input)
       outs[i] = currentOutput
       if i == 1 then
-         self.size:resize(currentOutput:dim()):copy(currentOutput:size())
+         self.outputSize:resize(currentOutput:dim()):copy(currentOutput:size())
       else
-         self.size[self.dimension] = self.size[self.dimension] + currentOutput:size(self.dimension)
-         for dim=1,self.size:size(1) do
+         self.outputSize[self.dimension] = self.outputSize[self.dimension] + currentOutput:size(self.dimension)
+         for dim=1,self.outputSize:size(1) do
             if dim ~= self.dimension then
                -- take the maximum size (shouldn't change anything for batch dim)
-               self.size[dim] = math.max(self.size[dim], currentOutput:size(dim))
+               self.outputSize[dim] = math.max(self.outputSize[dim], currentOutput:size(dim))
             end
          end
       end
    end
-   self.output:resize(self.size):zero() --zero for padding
+   self.output:resize(self.outputSize):zero() --zero for padding
 
    local offset = 1
    for i,module in ipairs(self.modules) do
diff --git a/Dropout.lua b/Dropout.lua
index 946c37f..15f2f46 100644
--- a/Dropout.lua
+++ b/Dropout.lua
@@ -1,10 +1,11 @@
 local Dropout, Parent = torch.class('nn.Dropout', 'nn.Module')
 
-function Dropout:__init(p,v1,inplace)
+function Dropout:__init(p,v1,inplace,stochasticInference)
    Parent.__init(self)
    self.p = p or 0.5
    self.train = true
    self.inplace = inplace
+   self.stochastic_inference = stochasticInference or false
    -- version 2 scales output during training instead of evaluation
    self.v2 = not v1
    if self.p >= 1 or self.p < 0 then
@@ -20,7 +21,7 @@ function Dropout:updateOutput(input)
       self.output:resizeAs(input):copy(input)
    end
    if self.p > 0 then
-      if self.train then
+      if self.train or self.stochastic_inference then
          self.noise:resizeAs(input)
          self.noise:bernoulli(1-self.p)
          if self.v2 then
diff --git a/JoinTable.lua b/JoinTable.lua
index 0d20fb9..6ab68e1 100644
--- a/JoinTable.lua
+++ b/JoinTable.lua
@@ -11,7 +11,7 @@ end
 function JoinTable:_getPositiveDimension(input)
    local dimension = self.dimension
    if dimension < 0 then
-      dimension = input:dim() + dimension + 1
+      dimension = input[1]:dim() + dimension + 1
    elseif self.nInputDims and input[1]:dim()==(self.nInputDims+1) then
       dimension = dimension + 1
    end
diff --git a/LookupTable.lua b/LookupTable.lua
index 8a60354..cf9c687 100644
--- a/LookupTable.lua
+++ b/LookupTable.lua
@@ -125,7 +125,7 @@ function LookupTable:renorm(input)
    if not self.maxNorm then
       return
    end
-   -- copy input into _input, so _input is continous.
+   -- copy input into _input, so _input is continuous.
    -- The copied _input will be modified in the C code.
    self._input:resize(input:size()):copy(input)
    local row_idx = self._input
diff --git a/MapTable.lua b/MapTable.lua
new file mode 100644
index 0000000..79b967d
--- /dev/null
+++ b/MapTable.lua
@@ -0,0 +1,99 @@
+local MapTable, parent = torch.class('nn.MapTable', 'nn.Container')
+
+function MapTable:__init(module, shared)
+   parent.__init(self)
+   self.shared = shared or {'weight', 'bias', 'gradWeight', 'gradBias'}
+   self.output = {}
+   self.gradInput = {}
+   self:add(module)
+end
+
+function MapTable:_extend(n)
+   self.modules[1] = self.module
+   for i = 2, n do
+      if not self.modules[i] then
+         self.modules[i] = self.module:clone(table.unpack(self.shared))
+      end
+   end
+end
+
+function MapTable:resize(n)
+   self:_extend(n)
+   for i = n + 1, #self.modules do
+      self.modules[i] = nil
+   end
+end
+
+function MapTable:add(module)
+   assert(not self.module, 'Single module required')
+   self.module = module
+   self.modules[1] = self.module
+   return self
+end
+
+function MapTable:updateOutput(input)
+   self.output = {}
+   self:_extend(#input)
+   for i = 1, #input do
+      self.output[i] = self:rethrowErrors(self.modules[i], i, 'updateOutput', input[i])
+   end
+   return self.output
+end
+
+function MapTable:updateGradInput(input, gradOutput)
+   self.gradInput = {}
+   self:_extend(#input)
+   for i = 1, #input do
+      self.gradInput[i] = self:rethrowErrors(self.modules[i], i, 'updateGradInput', input[i], gradOutput[i])
+   end
+   return self.gradInput
+end
+
+function MapTable:accGradParameters(input, gradOutput, scale)
+   scale = scale or 1
+   self:_extend(#input)
+   for i = 1, #input do
+      self:rethrowErrors(self.modules[i], i, 'accGradParameters', input[i], gradOutput[i], scale)
+   end
+end
+
+function MapTable:accUpdateGradParameters(input, gradOutput, lr)
+   lr = lr or 1
+   self:_extend(#input)
+   for i = 1, #input do
+      self:rethrowErrors(self.modules[i], i, 'accUpdateGradParameters', input[i], gradOutput[i], lr)
+   end
+end
+
+function MapTable:zeroGradParameters()
+    if self.module then
+        self.module:zeroGradParameters()
+    end
+end
+
+function MapTable:updateParameters(learningRate)
+    if self.module then
+        self.module:updateParameters(learningRate)
+    end
+end
+
+function MapTable:clearState()
+   for i = 2, #self.modules do
+      self.modules[i] = nil
+   end
+   parent.clearState(self)
+end
+
+function MapTable:__tostring__()
+   local tab = '  '
+   local line = '\n'
+   local extlast = '      '
+   local str = torch.type(self)
+   if self.module then
+      str = str .. ' {' .. line .. tab
+      str = str .. tostring(self.module):gsub(line, line .. tab .. extlast) .. line .. '}'
+   else
+      str = str .. ' { }'
+   end
+   return str
+end
diff --git a/Max.lua b/Max.lua
index 691fe9d..1392d8a 100644
--- a/Max.lua
+++ b/Max.lua
@@ -21,7 +21,7 @@ end
 function Max:_lazyInit()
    self._output = self._output or self.output.new()
    self._indices = self._indices or
-      (torch.type(self.output) == 'torch.CudaTensor' and torch.CudaTensor() or torch.LongTensor())
+      (torch.type(self.output) == 'torch.CudaTensor' and torch.CudaLongTensor() or torch.LongTensor())
 end
 
 function Max:updateOutput(input)
@@ -50,18 +50,9 @@ function Max:updateGradInput(input, gradOutput)
 end
 
 function Max:type(type, tensorCache)
-  -- torch.max expects a LongTensor as indices, whereas cutorch.max expects a CudaTensor.
-  if type == 'torch.CudaTensor' then
+    self._indices = nil
     parent.type(self, type, tensorCache)
-  else
-    -- self._indices must be a LongTensor. Setting it to nil temporarily avoids
-    -- unnecessary memory allocations.
-    local indices
-    indices, self._indices = self._indices, nil
-    parent.type(self, type, tensorCache)
-    self._indices = indices and indices:long() or nil
-  end
-  return self
+    return self
 end
 
 function Max:clearState()
diff --git a/Min.lua b/Min.lua
index f1d2b45..dc07cf9 100644
--- a/Min.lua
+++ b/Min.lua
@@ -21,7 +21,7 @@ end
 function Min:_lazyInit()
    self._output = self._output or self.output.new()
    self._indices = self._indices or
-      (torch.type(self.output) == 'torch.CudaTensor' and torch.CudaTensor() or torch.LongTensor())
+      (torch.type(self.output) == 'torch.CudaTensor' and torch.CudaLongTensor() or torch.LongTensor())
 end
 
 function Min:updateOutput(input)
@@ -50,18 +50,9 @@ function Min:updateGradInput(input, gradOutput)
 end
 
 function Min:type(type, tensorCache)
-  -- torch.min expects a LongTensor as indices, whereas cutorch.max expects a CudaTensor.
-  if type == 'torch.CudaTensor' then
+    self._indices = nil
     parent.type(self, type, tensorCache)
-  else
-    -- self._indices must be a LongTensor. Setting it to nil temporarily avoids
-    -- unnecessary memory allocations.
-    local indices
-    indices, self._indices = self._indices, nil
-    parent.type(self, type, tensorCache)
-    self._indices = indices and indices:long() or nil
-  end
-  return self
+    return self
 end
 
 function Min:clearState()
diff --git a/Normalize.lua b/Normalize.lua
index 24c1d07..5cd4857 100644
--- a/Normalize.lua
+++ b/Normalize.lua
@@ -25,7 +25,7 @@ function Normalize:updateOutput(input)
     -- specialization for the infinity norm
     self._indices = self._indices or
       (torch.type(self.output) == 'torch.CudaTensor' and
-       torch.CudaTensor() or torch.LongTensor())
+       torch.CudaLongTensor() or torch.LongTensor())
 
     self.buffer:abs(input)
     torch.max(self.norm, self._indices, self.buffer, 2)
@@ -127,18 +127,9 @@ function Normalize:__tostring__()
 end
 
 function Normalize:type(type, tensorCache)
-  -- torch.max expects a LongTensor as indices, whereas cutorch.max expects a CudaTensor.
-  if type == 'torch.CudaTensor' then
+    self._indices = nil
     parent.type(self, type, tensorCache)
-  else
-    -- self._indices must be a LongTensor. Setting it to nil temporarily avoids
-    -- unnecessary memory allocations.
-    local indices
-    indices, self._indices = self._indices, nil
-    parent.type(self, type, tensorCache)
-    self._indices = indices and indices:long() or nil
-  end
-  return self
+    return self
 end
 
 function Normalize:clearState()
diff --git a/SpatialConvolution.lua b/SpatialConvolution.lua
index 8324f95..01a08cd 100644
--- a/SpatialConvolution.lua
+++ b/SpatialConvolution.lua
@@ -89,25 +89,9 @@ local function makeContiguous(self, input, gradOutput)
    return input, gradOutput
 end
 
--- function to re-view the weight layout in a way that would make the MM ops happy
-local function viewWeight(self)
-   self.weight = self.weight:view(self.nOutputPlane, self.nInputPlane * self.kH * self.kW)
-   if self.gradWeight and self.gradWeight:dim() > 0 then
-      self.gradWeight = self.gradWeight:view(self.nOutputPlane, self.nInputPlane * self.kH * self.kW)
-   end
-end
-
-local function unviewWeight(self)
-   self.weight = self.weight:view(self.nOutputPlane, self.nInputPlane, self.kH, self.kW)
-   if self.gradWeight and self.gradWeight:dim() > 0 then
-      self.gradWeight = self.gradWeight:view(self.nOutputPlane, self.nInputPlane, self.kH, self.kW)
-   end
-end
-
 function SpatialConvolution:updateOutput(input)
    assert(input.THNN, torch.type(input)..'.THNN backend not imported')
    backCompatibility(self)
-   viewWeight(self)
    input = makeContiguous(self, input)
    input.THNN.SpatialConvolutionMM_updateOutput(
       input:cdata(),
@@ -120,7 +104,6 @@ function SpatialConvolution:updateOutput(input)
       self.dW, self.dH,
       self.padW, self.padH
    )
-   unviewWeight(self)
    return self.output
 end
 
@@ -128,20 +111,18 @@ function SpatialConvolution:updateGradInput(input, gradOutput)
    assert(input.THNN, torch.type(input)..'.THNN backend not imported')
    if self.gradInput then
       backCompatibility(self)
-      viewWeight(self)
       input, gradOutput = makeContiguous(self, input, gradOutput)
       input.THNN.SpatialConvolutionMM_updateGradInput(
          input:cdata(),
          gradOutput:cdata(),
          self.gradInput:cdata(),
-         self.weight:cdata(),         
+         self.weight:cdata(),
          self.finput:cdata(),
          self.fgradInput:cdata(),
          self.kW, self.kH,
          self.dW, self.dH,
          self.padW, self.padH
       )
-      unviewWeight(self)
       return self.gradInput
    end
 end
@@ -151,7 +132,6 @@ function SpatialConvolution:accGradParameters(input, gradOutput, scale)
    scale = scale or 1
    backCompatibility(self)
    input, gradOutput = makeContiguous(self, input, gradOutput)
-   viewWeight(self)
    input.THNN.SpatialConvolutionMM_accGradParameters(
       input:cdata(),
       gradOutput:cdata(),
@@ -164,7 +144,6 @@ function SpatialConvolution:accGradParameters(input, gradOutput, scale)
       self.padW, self.padH,
       scale
    )
-   unviewWeight(self)
 end
 
 function SpatialConvolution:type(type,tensorCache)
diff --git a/SpatialDilatedMaxPooling.lua b/SpatialDilatedMaxPooling.lua
index 929459c..2f0eba0 100644
--- a/SpatialDilatedMaxPooling.lua
+++ b/SpatialDilatedMaxPooling.lua
@@ -15,7 +15,7 @@ function SpatialDilatedMaxPooling:updateOutput(input)
    self.iheight = input:size(dims-1)
    self.iwidth = input:size(dims)
 
-   input.THNN.SpatialMaxPooling_updateOutput(
+   input.THNN.SpatialDilatedMaxPooling_updateOutput(
       input:cdata(),
       self.output:cdata(),
       self.indices:cdata(),
@@ -29,7 +29,7 @@ function SpatialDilatedMaxPooling:updateOutput(input)
 end
 
 function SpatialDilatedMaxPooling:updateGradInput(input, gradOutput)
-   input.THNN.SpatialMaxPooling_updateGradInput(
+   input.THNN.SpatialDilatedMaxPooling_updateGradInput(
       input:cdata(),
       gradOutput:cdata(),
       self.gradInput:cdata(),
diff --git a/SpatialDropout.lua b/SpatialDropout.lua
index 35daa18..4320061 100644
--- a/SpatialDropout.lua
+++ b/SpatialDropout.lua
@@ -1,15 +1,16 @@
 local SpatialDropout, Parent = torch.class('nn.SpatialDropout', 'nn.Module')
 
-function SpatialDropout:__init(p)
+function SpatialDropout:__init(p,stochasticInference)
    Parent.__init(self)
    self.p = p or 0.5
    self.train = true
+   self.stochastic_inference = stochasticInference or false
    self.noise = torch.Tensor()
 end
 
 function SpatialDropout:updateOutput(input)
    self.output:resizeAs(input):copy(input)
-   if self.train then
+   if self.train or self.stochastic_inference then
       if input:dim() == 4 then
         self.noise:resize(input:size(1), input:size(2), 1, 1)
       elseif input:dim() == 3 then
@@ -19,7 +20,7 @@ function SpatialDropout:updateOutput(input)
       end
       self.noise:bernoulli(1-self.p)
       -- We expand the random dropouts to the entire feature map because the
-      -- features are likely correlated accross the map and so the dropout
+      -- features are likely correlated across the map and so the dropout
       -- should also be correlated.
       self.output:cmul(torch.expandAs(self.noise, input))
    else
diff --git a/SpatialMaxPooling.lua b/SpatialMaxPooling.lua
index c05a876..8475b13 100644
--- a/SpatialMaxPooling.lua
+++ b/SpatialMaxPooling.lua
@@ -46,7 +46,6 @@ function SpatialMaxPooling:updateOutput(input)
       self.kW, self.kH,
       self.dW, self.dH,
       self.padW, self.padH,
-      1, 1,
       self.ceil_mode
    )
    return self.output
@@ -61,7 +60,6 @@ function SpatialMaxPooling:updateGradInput(input, gradOutput)
       self.kW, self.kH,
       self.dW, self.dH,
       self.padW, self.padH,
-      1, 1,
       self.ceil_mode
    )
    return self.gradInput
diff --git a/Sum.lua b/Sum.lua
index 5d61c28..9ff73f8 100644
--- a/Sum.lua
+++ b/Sum.lua
@@ -36,8 +36,8 @@ end
 
 function Sum:updateGradInput(input, gradOutput)
     local dimension = self:_getPositiveDimension(input)
-    -- zero-strides dont work with MKL/BLAS, so
-    -- dont set self.gradInput to zero-stride tensor.
+    -- zero-strides don't work with MKL/BLAS, so
+    -- don't set self.gradInput to zero-stride tensor.
     -- Instead, do a deepcopy
     local size      = input:size()
     size[dimension] = 1
diff --git a/THNN.lua b/THNN.lua
index e18dbaa..9100239 100644
--- a/THNN.lua
+++ b/THNN.lua
@@ -2,6 +2,7 @@ local ffi = require 'ffi'
 
 local THNN = {}
 
+
 local generic_THNN_h = require 'nn.THNN_h'
 -- strip all lines starting with #
 -- to remove preprocessor directives originally present
diff --git a/TemporalDynamicKMaxPooling.lua b/TemporalDynamicKMaxPooling.lua
new file mode 100644
index 0000000..511275b
--- /dev/null
+++ b/TemporalDynamicKMaxPooling.lua
@@ -0,0 +1,65 @@
+--[[
+   This file implements Dynamic K Max Pooling as described in the paper:
+   "A Convolutional Neural Network for Modelling Sentences"
+                   by Nal Kalchbrenner, Edward Grefenstette, Phil Blunsom
+   
+   The operation is simply selecting the k highest values out of a sequence.
+   k can be a calculated value or pre-defined
+
+   The value of k can be calulated as in the paper by using:
+      k_top as minK
+      (L-l)/L as factor
+   
+   Where:
+      k_top is the desired sequence length at the end of the convolution part,
+      L is the total number of layers,
+      l is this layers number
+]]
+
+local TemporalDynamicKMaxPooling, parent = torch.class('nn.TemporalDynamicKMaxPooling', 'nn.Module')
+
+function TemporalDynamicKMaxPooling:__init(minK, factor)
+   parent.__init(self)
+
+   self.minK = minK
+   self.factor = factor or 0
+end
+
+function TemporalDynamicKMaxPooling:updateOutput(input)
+   assert(input:dim() == 2 or input:dim() == 3, 'Only 2D or 3D(batch mode) accepted')
+
+   local seqDim = input:dim()-1
+   local k = math.max(self.minK, math.ceil(self.factor*input:size(seqDim)))
+   assert(input:size(seqDim) >= self.minK, 'Input sequence length (' .. input:size(seqDim) .. ') too small for desired k value (' .. k .. ')')
+
+   -- Sort input in descending order
+   local sorted, allIndices = input:sort(seqDim,true)
+   -- Reduce the indices to only include the top-k and return to original order by sorting
+   self.indices = allIndices:narrow(seqDim, 1, k):sort(seqDim)
+
+   self.output = input:gather(seqDim, self.indices)
+
+   return self.output
+end
+
+function TemporalDynamicKMaxPooling:updateGradInput(input, gradOutput)
+   if self.gradInput then
+      local seqDim = input:dim()-1
+
+      self.gradInput:resizeAs(input)
+      self.gradInput:zero()
+
+      -- Using the previously stored indices, add the gradOutputs to their respective
+      -- input indices in the self.gradInput buffer
+      local updateValues = self.gradInput:gather(seqDim, self.indices)
+      updateValues:add(gradOutput)
+      self.gradInput:scatter(seqDim, self.indices, updateValues)
+
+      return self.gradInput
+   end
+end
+
+function TemporalDynamicKMaxPooling:clearState()
+   nn.utils.clear(self, 'indices')
+   return parent.clearState(self)
+end
diff --git a/Threshold.lua b/Threshold.lua
index 0c22bae..6fdd264 100644
--- a/Threshold.lua
+++ b/Threshold.lua
@@ -34,6 +34,7 @@ function Threshold:updateGradInput(input, gradOutput)
       gradOutput:cdata(),
       self.gradInput:cdata(),
       self.threshold,
+      self.val,
       self.inplace
    )
    return self.gradInput
diff --git a/VolumetricDilatedMaxPooling.lua b/VolumetricDilatedMaxPooling.lua
new file mode 100644
index 0000000..050e2c9
--- /dev/null
+++ b/VolumetricDilatedMaxPooling.lua
@@ -0,0 +1,64 @@
+local THNN = require 'nn.THNN'
+local VolumetricDilatedMaxPooling, parent = torch.class('nn.VolumetricDilatedMaxPooling', 'nn.VolumetricMaxPooling')
+
+function VolumetricDilatedMaxPooling:__init(kT, kW, kH, dT, dW, dH, padT, padW, padH, dilationT, dilationW, dilationH)
+   parent.__init(self, kT, kW, kH, dT, dW, dH, padT, padW, padH)
+
+   self.dilationT = dilationT or 1
+   self.dilationW = dilationW or 1
+   self.dilationH = dilationH or 1
+
+end
+
+function VolumetricDilatedMaxPooling:updateOutput(input)
+   local dims = input:dim()
+   self.itime = input:size(dims-2)
+   self.iheight = input:size(dims-1)
+   self.iwidth = input:size(dims)
+
+   self.indices = self.indices or input.new()
+   input.THNN.VolumetricDilatedMaxPooling_updateOutput(
+      input:cdata(),
+      self.output:cdata(),
+      self.indices:cdata(),
+      self.kT, self.kW, self.kH,
+      self.dT, self.dW, self.dH,
+      self.padT, self.padW, self.padH,
+      self.dilationT, self.dilationW, self.dilationH,
+      self.ceil_mode
+   )
+   return self.output
+end
+
+function VolumetricDilatedMaxPooling:updateGradInput(input, gradOutput)
+   input.THNN.VolumetricDilatedMaxPooling_updateGradInput(
+      input:cdata(),
+      gradOutput:cdata(),
+      self.gradInput:cdata(),
+      self.indices:cdata(),
+      self.dT, self.dW, self.dH,
+      self.padT, self.padW, self.padH,
+      self.dilationT, self.dilationW, self.dilationH
+   )
+   return self.gradInput
+end
+
+function VolumetricDilatedMaxPooling:clearState()
+   if self.indices then 
+      self.indices:set() 
+   end
+   return parent.clearState(self)
+end
+
+function VolumetricDilatedMaxPooling:__tostring__()
+   local s =  string.format('%s(%dx%dx%d, %d,%d,%d', torch.type(self),
+                            self.kT, self.kW, self.kH, self.dT, self.dW, self.dH)
+   if (self.padT or self.padW or self.padH) and
+      (self.padT ~= 0 or self.padW ~= 0 or self.padH ~= 0) then
+      s = s .. ', ' .. self.padT.. ',' .. self.padW .. ','.. self.padH
+   end
+   s = s .. ', ' .. self.dilationT .. ',' .. self.dilationW .. ',' .. self.dilationH
+   s = s .. ')'
+
+   return s
+end
diff --git a/VolumetricDropout.lua b/VolumetricDropout.lua
index 5f495af..809e28a 100644
--- a/VolumetricDropout.lua
+++ b/VolumetricDropout.lua
@@ -1,15 +1,16 @@
 local VolumetricDropout, Parent = torch.class('nn.VolumetricDropout', 'nn.Module')
 
-function VolumetricDropout:__init(p)
+function VolumetricDropout:__init(p,stochasticInference)
    Parent.__init(self)
    self.p = p or 0.5
    self.train = true
+   self.stochastic_inference = stochasticInference or false
    self.noise = torch.Tensor()
 end
 
 function VolumetricDropout:updateOutput(input)
    self.output:resizeAs(input):copy(input)
-   if self.train then
+   if self.train or self.stochastic_inference then
       if input:dim() == 5 then
         self.noise:resize(input:size(1), input:size(2), 1, 1, 1)
       elseif input:dim() == 4 then
@@ -19,7 +20,7 @@ function VolumetricDropout:updateOutput(input)
       end
       self.noise:bernoulli(1-self.p)
       -- We expand the random dropouts to the entire feature map because the
-      -- features are likely correlated accross the map and so the dropout
+      -- features are likely correlated across the map and so the dropout
       -- should also be correlated.
       self.output:cmul(torch.expandAs(self.noise, input))
    else
diff --git a/doc/convolution.md b/doc/convolution.md
index 96d92d9..b1a0d4c 100644
--- a/doc/convolution.md
+++ b/doc/convolution.md
@@ -37,6 +37,7 @@ a kernel for computing the weighted average in a neighborhood ;
     * [VolumetricFullConvolution](#nn.VolumetricFullConvolution) : a 3D full convolution over an input video (a sequence of images) ;
     * [VolumetricDilatedConvolution](#nn.VolumetricDilatedConvolution) : a 3D dilated convolution over an input image ;
     * [VolumetricMaxPooling](#nn.VolumetricMaxPooling) : a 3D max-pooling operation over an input video.
+    * [VolumetricDilatedMaxPooling](#nn.VolumetricDilatedMaxPooling) : a 3D dilated max-pooling operation over an input video ;
     * [VolumetricAveragePooling](#nn.VolumetricAveragePooling) : a 3D average-pooling operation over an input video.
     * [VolumetricMaxUnpooling](#nn.VolumetricMaxUnpooling) : a 3D max-unpooling operation.
     * [VolumetricReplicationPadding](#nn.VolumetricReplicationPadding) : Pads a volumetric feature map with the value at the edge of the input borders. ;
@@ -1022,6 +1023,30 @@ Applies 3D max-pooling operation in `kTxkWxkH` regions by step size
 `dTxdWxdH` steps. The number of output features is equal to the number of
 input planes / dT. The input can optionally be padded with zeros. Padding should be smaller than half of kernel size.  That is, `padT < kT/2`, `padW < kW/2` and `padH < kH/2`.
 
+<a name="nn.VolumetricDilatedMaxPooling"></a>
+### VolumetricDilatedMaxPooling ###
+
+```lua
+module = nn.VolumetricDilatedMaxPooling(kT, kW, kH [, dT, dW, dH, padT, padW, padH, dilationT, dilationW, dilationH])
+```
+
+Also sometimes referred to as **atrous pooling**.
+Applies 3D dilated max-pooling operation in `kTxkWxkH` regions by step size
+`dTxdWxdH` steps. The number of output features is equal to the number of
+input planes. If `dilationT`, `dilationW` and `dilationH` are not provided, this is equivalent to performing normal `nn.VolumetricMaxPooling`.
+
+If the input image is a 4D tensor `nInputPlane x depth x height x width`, the output
+image size will be `nOutputPlane x otime x oheight x owidth` where
+
+```lua
+otime  = op((depth - (dilationT * (kT - 1) + 1) + 2*padT) / dT + 1)
+owidth  = op((width - (dilationW * (kW - 1) + 1) + 2*padW) / dW + 1)
+oheight = op((height - (dilationH * (kH - 1) + 1) + 2*padH) / dH + 1)
+```
+
+`op` is a rounding operator. By default, it is `floor`. It can be changed
+by calling `:ceil()` or `:floor()` methods.
+
 <a name="nn.VolumetricAveragePooling"></a>
 ### VolumetricAveragePooling ###
 
diff --git a/doc/simple.md b/doc/simple.md
index 6f01a56..302e4d8 100644
--- a/doc/simple.md
+++ b/doc/simple.md
@@ -27,8 +27,8 @@ Simple Modules are used for various tasks like adapting Tensor methods and provi
     * [Unsqueeze](#nn.Unsqueeze) : unsqueeze the input, i.e., insert singleton dimension;  
     * [Transpose](#nn.Transpose) : [transposes](https://github.com/torch/torch7/blob/master/doc/tensor.md#tensor-transposedim1-dim2) the input ;
   * Modules that adapt mathematical Tensor methods :
-    * [AddConstant](https://github.com/torch/nn/blob/master/doc/transfer.md#nn.AddConstant) : adding a constant ;
-    * [MulConstant](https://github.com/torch/nn/blob/master/doc/transfer.md#nn.MulConstant) : multiplying a constant ;
+    * [AddConstant](https://github.com/torch/nn/blob/master/doc/transfer.md#addconstant) : adding a constant ;
+    * [MulConstant](https://github.com/torch/nn/blob/master/doc/transfer.md#mulconstant) : multiplying a constant ;
     * [Max](#nn.Max) : a [max](https://github.com/torch/torch7/blob/master/doc/maths.md#torch.max) operation over a given dimension ;
     * [Min](#nn.Min) : a [min](https://github.com/torch/torch7/blob/master/doc/maths.md#torchminresval-resind-x) operation over a given dimension ;
     * [Mean](#nn.Mean) : a [mean](https://github.com/torch/torch7/blob/master/doc/maths.md#res-torchmeanres-x-dim) operation over a given dimension ;
@@ -52,6 +52,7 @@ Simple Modules are used for various tasks like adapting Tensor methods and provi
     * [L1Penalty](#nn.L1Penalty) : adds an L1 penalty to an input (for sparsity) ;
     * [GradientReversal](#nn.GradientReversal) : reverses the gradient (to maximize an objective function) ;
     * [GPU](#nn.GPU) : decorates a module so that it can be executed on a specific GPU device.
+    * [TemporalDynamicKMaxPooling](#nn.TemporalDynamicKMaxPooling) : selects the k highest values in a sequence. k can be calculated based on sequence length ;
 
 <a name="nn.Linear"></a>
 ## Linear ##
@@ -138,7 +139,7 @@ module = nn.Bilinear(inputDimension1, inputDimension2, outputDimension, [bias =
 ```
 
 Applies a bilinear transformation to the incoming data, i.e. `\forall k: y_k = x_1 A_k x_2 + b`. The `input` tensor given in `forward(input)` is a table containing both inputs `x_1` and `x_2`, which are tensors of size `N x inputDimension1`
-and `N x inputDimension1`, respectively. The layer can be trained without biases by setting `bias = false`.
+and `N x inputDimension2`, respectively. The layer can be trained without biases by setting `bias = false`.
 
 You can create a layer in the following way:
 
@@ -246,6 +247,19 @@ During [evaluation](module.md#evaluate), `Dropout` does nothing more than forwar
 [torch.DoubleTensor of dimension 2x4]
 ```
 
+There is also an option for stochastic [evaluation](module.md#evaluate) which drops the `outputs` just like how it is done during [training](module.md#training):
+
+```lua
+module_stochastic_evaluation = nn.Dropout(nil, nil, nil, true)
+
+> module_stochastic_evaluation:evaluate()
+
+> module_stochastic_evaluation:forward(x)
+  2   4   6   0
+  0  12  14   0
+[torch.DoubleTensor of dimension 2x4]
+```
+
 We can return to training our model by first calling [Module:training()](module.md#training):
 
 ```lua
@@ -404,15 +418,16 @@ module = nn.CMul(size)
 ```
 
 Applies a component-wise multiplication to the incoming data, i.e. `y_i = w_i * x_i`. Argument `size` can be one or many numbers (sizes) or a `torch.LongStorage`. For example, `nn.CMul(3,4,5)` is equivalent to `nn.CMul(torch.LongStorage{3,4,5})`.
+If the size for a particular dimension is 1, the multiplication will be expanded along the entire axis.
 
 Example:
 
 ```lua
 mlp = nn.Sequential()
-mlp:add(nn.CMul(5))
+mlp:add(nn.CMul(5, 1))
 
-y = torch.Tensor(5)
-sc = torch.Tensor(5)
+y = torch.Tensor(5, 4)
+sc = torch.Tensor(5, 4)
 for i = 1, 5 do sc[i] = i; end -- scale input with this
 
 function gradUpdate(mlp, x, y, criterion, learningRate)
@@ -426,7 +441,7 @@ function gradUpdate(mlp, x, y, criterion, learningRate)
 end
 
 for i = 1, 10000 do
-   x = torch.rand(5)
+   x = torch.rand(5, 4)
    y:copy(x)
    y:cmul(sc)
    err = gradUpdate(mlp, x, y, nn.MSECriterion(), 0.01)
@@ -443,7 +458,7 @@ gives the output:
  3.0000
  4.0000
  5.0000
-[torch.Tensor of dimension 5]
+[torch.Tensor of dimension 5x1]
 ```
 
 i.e. the network successfully learns the input `x` has been scaled by those scaling factors to produce the output `y`.
@@ -598,7 +613,7 @@ end
 module = nn.Copy(inputType, outputType, [forceCopy, dontCast])
 ```
 
-This layer copies the input to output with type casting from `inputType` to `outputType`. Unless `forceCopy` is true, when the first two arguments are the same, the input isn't copied, only transfered as the output. The default `forceCopy` is false.
+This layer copies the input to output with type casting from `inputType` to `outputType`. Unless `forceCopy` is true, when the first two arguments are the same, the input isn't copied, only transferred as the output. The default `forceCopy` is false.
 When `dontCast` is true, a call to `nn.Copy:type(type)` will not cast the module's `output` and `gradInput` Tensors to the new type. The default is false.
 
 <a name="nn.Narrow"></a>
@@ -1039,7 +1054,7 @@ Setting `numInputDims` allows to use this module on batches.
 ```lua
 module = nn.Unsqueeze(pos [, numInputDims])
 ```
-Insert singleton dim (i.e., dimension 1) at position `pos`. 
+Insert singleton dim (i.e., dimension 1) at position `pos`.
 For an `input` with `dim = input:dim()`, there are `dim + 1` possible positions to insert the singleton dimension.
 For example, if `input` is `3` dimensional tensor in size `p x q x r`, then the singleton dim can be inserted at the following `4` positions
 ```
@@ -1070,7 +1085,7 @@ input2 = torch.Tensor(3, 5, 7) -- input2: 3 x 5 x 7
 m:forward(input2) -- output: 3 x 1 x 5 x 7
 ```
 
-Indicate the expected input feature map dimension by specifying `numInputDims`. 
+Indicate the expected input feature map dimension by specifying `numInputDims`.
 This allows the module to work with mini-batch. Example:
 ```lua
 b = 5 -- batch size 5
@@ -1413,14 +1428,14 @@ to set the hyper-parameter `lambda` dynamically during training.
 gpu = nn.GPU(module, device, [outdevice])
 require 'cunn'
 gpu:cuda()
-``` 
+```
 
 Decorates an encapsulated `module` so that it can be executed on a specific GPU `device`.
 The decorated module's `parameters` are thus hosted on the specified GPU `device`.
 All operations on the `gpu` module are executed on that device.
-Calls to `forward`/`backward` will transfer arguments `input` and `gradOutput` to the specified `device`, 
-which are then fed as arguments to the decorated `module`. 
-Returned `output` is located on the specified `outdevice` (defaults to `device`). 
+Calls to `forward`/`backward` will transfer arguments `input` and `gradOutput` to the specified `device`,
+which are then fed as arguments to the decorated `module`.
+Returned `output` is located on the specified `outdevice` (defaults to `device`).
 Returned `gradInput` is allocated on the same device as the `input`.
 
 When serialized/deserialized, the `gpu` module will be run on the same `device` that it was serialized with.
@@ -1429,16 +1444,16 @@ To prevent this from happening, the module can be converted to float/double befo
 ```lua
 gpu:float()
 gpustr = torch.serialize(gpu)
-``` 
+```
 
 The module is located in the __nn__ package instead of __cunn__ as this allows
-it to be used in CPU-only enviroments, which are common for production models.
+it to be used in CPU-only environments, which are common for production models.
 
 The module supports nested table `input` and `gradOutput` tensors originating from multiple devices.
-Each nested tensor in the returned `gradInput` will be transfered to the device its commensurate tensor in the `input`.
+Each nested tensor in the returned `gradInput` will be transferred to the device its commensurate tensor in the `input`.
 
-The intended use-case is not for model-parallelism where the models are executed in parallel on multiple devices, but 
-for sequential models where a single GPU doesn't have enough memory. 
+The intended use-case is not for model-parallelism where the models are executed in parallel on multiple devices, but
+for sequential models where a single GPU doesn't have enough memory.
 
 Example using 4 GPUs:
 
@@ -1448,7 +1463,20 @@ mlp = nn.Sequential()
    :add(nn.GPU(nn.Linear(10000,10000), 2))
    :add(nn.GPU(nn.Linear(10000,10000), 3))
    :add(nn.GPU(nn.Linear(10000,10000), 4, cutorch.getDevice()))
-``` 
+```
 
 Note how the last `GPU` instance will return an `output` tensor on the same device as the current device (`cutorch.getDevice`).
- 
+
+<a name="nn.TemporalDynamicKMaxPooling"></a>
+## TemporalDynamicKMaxPooling ##
+
+```lua
+module = nn.TemporalDynamicKMaxPooling(minK, [factor])
+```
+
+Selects the highest `k` values for each feature in the feature map sequence provided. The input sequence is composed of `nInputFrame` frames (i.e. `nInputFrame` is sequence length). The `input` tensor in `forward(input)` is expected to be a 2D tensor (`nInputFrame x inputFrameSize`) or a 3D tensor (`nBatchFrame x nInputFrame x inputFrameSize`), where `inputFrameSize` is the number of features across the sequence.
+
+If `factor` is not provided, `k = minK`, else the value of k is calculated with:
+```lua
+k = math.max(minK, math.ceil(factor*nInputFrame)))
+```
diff --git a/doc/table.md b/doc/table.md
index a2e23f8..ee61719 100644
--- a/doc/table.md
+++ b/doc/table.md
@@ -7,6 +7,7 @@ This allows one to build very rich architectures:
   * `table` Container Modules encapsulate sub-Modules:
     * [`ConcatTable`](#nn.ConcatTable): applies each member module to the same input     [`Tensor`](https://github.com/torch/torch7/blob/master/doc/tensor.md#tensor) and outputs a `table`;
     * [`ParallelTable`](#nn.ParallelTable): applies the `i`-th member module to the `i`-th input and outputs a `table`;
+    * [`MapTable`](#nn.MapTable): applies a single module to every input and outputs a `table`;
   * Table Conversion Modules convert between `table`s and `Tensor`s or `table`s:
     * [`SplitTable`](#nn.SplitTable): splits a `Tensor` into a `table` of `Tensor`s;
     * [`JoinTable`](#nn.JoinTable): joins a `table` of `Tensor`s into a `Tensor`;
@@ -23,6 +24,8 @@ This allows one to build very rich architectures:
     * [`CSubTable`](#nn.CSubTable): substraction of input `Tensor`s;
     * [`CMulTable`](#nn.CMulTable): multiplication of input `Tensor`s;
     * [`CDivTable`](#nn.CDivTable): division of input `Tensor`s;
+    * [`CMaxTable`](#nn.CMaxTable): max of input `Tensor`s;
+    * [`CMinTable`](#nn.CMinTable): min of input `Tensor`s;
   * `Table` of Criteria:
     * [`CriterionTable`](#nn.CriterionTable): wraps a [Criterion](criterion.md#nn.Criterion) so that it can accept a `table` of inputs.
 
@@ -165,6 +168,57 @@ which gives the output:
 ```
 
 
+<a name="nn.MapTable"></a>
+## MapTable ##
+
+```lua
+module = nn.MapTable(m, share)
+```
+
+`MapTable` is a container for a single module which will be applied to all input elements. The member module is cloned as necessary to process all input elements. Call `resize(n)` to set the number of clones manually or call `clearState()` to discard all clones.
+
+Optionally, the module can be initialized with the contained module and with a list of parameters that are shared across all clones. By default, these parameters are `weight`, `bias`, `gradWeight` and `gradBias`.
+
+```
++----------+         +-----------+
+| {input1, +---------> {member,  |
+|          |         |           |
+|  input2, +--------->  clone,   |
+|          |         |           |
+|  input3} +--------->  clone}   |
++----------+         +-----------+
+```
+
+### Example
+
+```lua
+map = nn.MapTable()
+map:add(nn.Linear(10, 3))
+
+x1 = torch.rand(10)
+x2 = torch.rand(10)
+y = map:forward{x1, x2}
+
+for i, k in pairs(y) do print(i, k) end
+```
+
+which gives the output:
+
+```lua
+1
+ 0.0345
+ 0.8695
+ 0.6502
+[torch.DoubleTensor of size 3]
+
+2
+ 0.0269
+ 0.4953
+ 0.2691
+[torch.DoubleTensor of size 3]
+```
+
+
 <a name="nn.SplitTable"></a>
 ## SplitTable ##
 
@@ -1212,3 +1266,30 @@ m = nn.CDivTable()
 [torch.DoubleTensor of dimension 5]
 ```
 
+<a name="nn.CMaxTable"></a>
+## CMaxTable ##
+
+Takes a `table` of `Tensor`s and outputs the max of all of them.
+
+```lua
+m = nn.CMaxTable()
+=m:forward({{torch.Tensor{1,2,3}, torch.Tensor{3,2,1}})
+ 3
+ 2
+ 3
+[torch.DoubleTensor of size 3]
+```
+
+<a name="nn.CMinTable"></a>
+## CMinTable ##
+
+Takes a `table` of `Tensor`s and outputs the min of all of them.
+
+```lua
+m = nn.CMinTable()
+=m:forward({{torch.Tensor{1,2,3}, torch.Tensor{3,2,1}})
+ 1
+ 2
+ 1
+[torch.DoubleTensor of size 3]
+```
diff --git a/init.lua b/init.lua
index a9c68da..70027a1 100644
--- a/init.lua
+++ b/init.lua
@@ -54,6 +54,8 @@ require('nn.CAddTable')
 require('nn.CDivTable')
 require('nn.CMulTable')
 require('nn.CSubTable')
+require('nn.CMaxTable')
+require('nn.CMinTable')
 
 require('nn.Euclidean')
 require('nn.WeightedEuclidean')
@@ -110,6 +112,7 @@ require('nn.SpatialAdaptiveMaxPooling')
 require('nn.TemporalConvolution')
 require('nn.TemporalSubSampling')
 require('nn.TemporalMaxPooling')
+require('nn.TemporalDynamicKMaxPooling')
 require('nn.SpatialSubtractiveNormalization')
 require('nn.SpatialDivisiveNormalization')
 require('nn.SpatialContrastiveNormalization')
@@ -125,6 +128,7 @@ require('nn.VolumetricConvolution')
 require('nn.VolumetricFullConvolution')
 require('nn.VolumetricDilatedConvolution')
 require('nn.VolumetricMaxPooling')
+require('nn.VolumetricDilatedMaxPooling')
 require('nn.VolumetricMaxUnpooling')
 require('nn.VolumetricAveragePooling')
 require('nn.VolumetricBatchNormalization')
@@ -142,6 +146,7 @@ require('nn.MixtureTable')
 require('nn.CriterionTable')
 require('nn.FlattenTable')
 require('nn.NarrowTable')
+require('nn.MapTable')
 
 require('nn.Criterion')
 require('nn.MSECriterion')
diff --git a/lib/THNN/generic/BCECriterion.c b/lib/THNN/generic/BCECriterion.c
new file mode 100644
index 0000000..c8d7da2
--- /dev/null
+++ b/lib/THNN/generic/BCECriterion.c
@@ -0,0 +1,50 @@
+#ifndef TH_GENERIC_FILE
+#define TH_GENERIC_FILE "generic/BCECriterion.c"
+#else
+
+#define EPS 1e-12
+
+void THNN_(BCECriterion_updateOutput)(THNNState *state, THTensor *input, THTensor *target, THTensor *output, bool sizeAverage, THTensor *weights)
+{
+  real sum = 0;
+
+  if(weights)
+    TH_TENSOR_APPLY3(real, input, real, target, real, weights,
+      real x = *input_data;
+      real y = *target_data;
+      real w = *weights_data;
+      sum -= (log(x + EPS) * y + log(1. - x + EPS) * (1. - y)) * w;
+    )
+  else
+    TH_TENSOR_APPLY2(real, input, real, target,
+      real x = *input_data;
+      real y = *target_data;
+      sum -= log(x + EPS) * y + log(1. - x + EPS) * (1. - y);
+    );
+
+
+  if (sizeAverage)
+    sum /= THTensor_(nElement)(input);
+
+  THTensor_(set1d)(output, 0, sum);
+}
+
+void THNN_(BCECriterion_updateGradInput)(THNNState *state, THTensor *input, THTensor *target, THTensor *gradInput, bool sizeAverage, THTensor *weights)
+{
+  real norm = (sizeAverage ? 1./((real)THTensor_(nElement)(input)) : 1.);
+
+  THTensor_(resizeAs)(gradInput, input);
+
+  TH_TENSOR_APPLY3(real, gradInput, real, input, real, target,
+    real x = *input_data;
+    real y = *target_data;
+    *gradInput_data = - norm * (y - x) / ((1. - x + EPS) * (x + EPS));
+  );
+
+  if(weights)
+    THTensor_(cmul)(gradInput, gradInput, weights);
+}
+
+#undef EPS
+
+#endif
diff --git a/lib/THNN/generic/HardTanh.c b/lib/THNN/generic/HardTanh.c
index 3b7ba3d..f360068 100644
--- a/lib/THNN/generic/HardTanh.c
+++ b/lib/THNN/generic/HardTanh.c
@@ -85,7 +85,7 @@ void THNN_(HardTanh_updateGradInput)(
     if (inplace)
     {
       TH_TENSOR_APPLY2(real, gradOutput, real, input,
-        if (*input_data < min_val || *input_data > max_val)
+        if (*input_data <= min_val || *input_data >= max_val)
           *gradOutput_data = 0;
       );
     }
diff --git a/lib/THNN/generic/SpatialConvolutionMM.c b/lib/THNN/generic/SpatialConvolutionMM.c
index e7460c8..64aa9db 100644
--- a/lib/THNN/generic/SpatialConvolutionMM.c
+++ b/lib/THNN/generic/SpatialConvolutionMM.c
@@ -67,9 +67,12 @@ void THNN_(SpatialConvolutionMM_updateOutput)(
   long outputWidth;
   long outputHeight;
 
+  int freeWeight = 0;
+
   THArgCheck( input->nDimension == 3 || input->nDimension == 4, 2, "3D or 4D (batch mode) tensor expected");
   THArgCheck(kW > 0 && kH > 0, 8, "kernel size should be greater than zero");
   THArgCheck(dW > 0 && dH > 0, 10, "stride should be greater than zero");
+  THArgCheck(weight->nDimension == 2 || weight->nDimension == 4, 4, "weight tensor should be 2D or 4D");
 
   if (input->nDimension == 4) {
     dimf++;
@@ -88,8 +91,19 @@ void THNN_(SpatialConvolutionMM_updateOutput)(
     THError("Given input size: (%dx%dx%d). Calculated output size: (%dx%dx%d). Output size is too small",
         nInputPlane,inputHeight,inputWidth,nOutputPlane,outputHeight,outputWidth);
 
-  if (nInputPlane*kW*kH != weight->size[1])
-    THError("Wrong number of input channels! Input has %d channels, expected %d",nInputPlane,weight->size[1]/(kW*kH));
+
+  int expectedWeightSize = weight->nDimension == 2 ? nInputPlane*kW*kH : nInputPlane;
+  int weightInputPlanes = weight->nDimension == 2 ? weight->size[1]/(kW*kH) : weight->size[1];
+  if (expectedWeightSize != weight->size[1])
+    THError("Wrong number of input channels! Input has %d channels, expected %d",
+        nInputPlane, weightInputPlanes);
+
+  if (weight->nDimension == 4) {
+    long s1 = weight->size[0];
+    long s2 = weight->size[1] * weight->size[2] * weight->size[3];
+    weight = THTensor_(newWithStorage2d)(weight->storage, weight->storageOffset, s1, -1, s2, -1);
+    freeWeight = 1;
+  }
 
   if(input->nDimension == 3)
   {
@@ -126,6 +140,9 @@ void THNN_(SpatialConvolutionMM_updateOutput)(
       THTensor_(free)(finput_t);
     }
   }
+
+  if (freeWeight)
+    THTensor_(free)(weight);
 }
 
 static void THNN_(SpatialConvolutionMM_updateGradInput_frame)(
@@ -167,17 +184,27 @@ void THNN_(SpatialConvolutionMM_updateGradInput)(
           int padH)
 {
   long nOutputPlane = weight->size[0];
+  int freeWeight = 0;
 
   THArgCheck( nOutputPlane == gradOutput->size[input->nDimension == 4 ? 1 : 0], 3, "Number of output features is not equal to nOutputPlane" );
   THArgCheck(kW > 0 && kH > 0, 9, "kernel size should be greater than zero");
   THArgCheck(dW > 0 && dH > 0, 11, "stride should be greater than zero");
+  THArgCheck(weight->nDimension == 2 || weight->nDimension == 4, 4, "weight tensor should be 2D or 4D");
 
   THTensor_(resizeAs)(gradInput, input);
   THTensor_(resizeAs)(fgradInput, finput);
   // depending on the BLAS library, fgradInput (result tensor) might
   // be left uninitialized on zero alpha, which might lead to weird behavior
   // hence, to be safe, zero it
-  THTensor_(zero)(fgradInput); 
+  THTensor_(zero)(fgradInput);
+
+  if (weight->nDimension == 4) {
+    long s1 = weight->size[0];
+    long s2 = weight->size[1] * weight->size[2] * weight->size[3];
+    weight = THTensor_(newWithStorage2d)(weight->storage, weight->storageOffset, s1, -1, s2, -1);
+    freeWeight = 1;
+  }
+
   THTensor_(transpose)(weight, weight, 0, 1);
 
   if(input->nDimension == 3)
@@ -205,6 +232,9 @@ void THNN_(SpatialConvolutionMM_updateGradInput)(
   }
 
   THTensor_(transpose)(weight, weight, 0, 1);
+
+  if (freeWeight)
+    THTensor_(free)(weight);
 }
 
 static void THNN_(SpatialConvolutionMM_accGradParameters_frame)(
@@ -254,10 +284,19 @@ void THNN_(SpatialConvolutionMM_accGradParameters)(
           int padH,
           real scale)
 {
+  int freeWeight = 0;
   long nOutputPlane = gradWeight->size[0];
   THArgCheck( nOutputPlane == gradOutput->size[input->nDimension == 4 ? 1 : 0], 3, "Number of output features is not equal to nOutputPlane" );
   THArgCheck(kW > 0 && kH > 0, 8, "kernel size should be greater than zero");
   THArgCheck(dW > 0 && dH > 0, 10, "stride should be greater than zero");
+  THArgCheck(gradWeight->nDimension == 2 || gradWeight->nDimension == 4, 4, "gradWeight tensor should be 2D or 4D");
+
+  if (gradWeight->nDimension == 4) {
+    long s1 = gradWeight->size[0];
+    long s2 = gradWeight->size[1] * gradWeight->size[2] * gradWeight->size[3];
+    gradWeight = THTensor_(newWithStorage2d)(gradWeight->storage, gradWeight->storageOffset, s1, -1, s2, -1);
+    freeWeight = 1;
+  }
 
   if(input->nDimension == 3)
   {
@@ -279,6 +318,8 @@ void THNN_(SpatialConvolutionMM_accGradParameters)(
       THTensor_(free)(finput_t);
     }
   }
+  if (freeWeight)
+    THTensor_(free)(gradWeight);
 }
 
 #endif
diff --git a/lib/THNN/generic/SpatialMaxPooling.c b/lib/THNN/generic/SpatialDilatedMaxPooling.c
similarity index 91%
copy from lib/THNN/generic/SpatialMaxPooling.c
copy to lib/THNN/generic/SpatialDilatedMaxPooling.c
index 3daef1d..6500f49 100644
--- a/lib/THNN/generic/SpatialMaxPooling.c
+++ b/lib/THNN/generic/SpatialDilatedMaxPooling.c
@@ -1,8 +1,8 @@
 #ifndef TH_GENERIC_FILE
-#define TH_GENERIC_FILE "generic/SpatialMaxPooling.c"
+#define TH_GENERIC_FILE "generic/SpatialDilatedMaxPooling.c"
 #else
 
-static void THNN_(SpatialMaxPooling_updateOutput_frame)(
+static void THNN_(SpatialDilatedMaxPooling_updateOutput_frame)(
           real *input_p,
           real *output_p,
           real *ind_p,
@@ -74,7 +74,7 @@ static void THNN_(SpatialMaxPooling_updateOutput_frame)(
   }
 }
 
-void THNN_(SpatialMaxPooling_updateOutput)(
+void THNN_(SpatialDilatedMaxPooling_updateOutput)(
           THNNState *state,
           THTensor *input,
           THTensor *output,
@@ -155,7 +155,7 @@ void THNN_(SpatialMaxPooling_updateOutput)(
     output_data = THTensor_(data)(output);
     indices_data = THTensor_(data)(indices);
 
-    THNN_(SpatialMaxPooling_updateOutput_frame)(input_data, output_data,
+    THNN_(SpatialDilatedMaxPooling_updateOutput_frame)(input_data, output_data,
                                               indices_data,
                                               nslices,
                                               iwidth, iheight,
@@ -180,7 +180,7 @@ void THNN_(SpatialMaxPooling_updateOutput)(
 #pragma omp parallel for private(p)
     for (p = 0; p < nbatch; p++)
     {
-      THNN_(SpatialMaxPooling_updateOutput_frame)(input_data+p*nslices*iwidth*iheight, output_data+p*nslices*owidth*oheight,
+      THNN_(SpatialDilatedMaxPooling_updateOutput_frame)(input_data+p*nslices*iwidth*iheight, output_data+p*nslices*owidth*oheight,
                                                 indices_data+p*nslices*owidth*oheight,
                                                 nslices,
                                                 iwidth, iheight,
@@ -196,7 +196,7 @@ void THNN_(SpatialMaxPooling_updateOutput)(
   THTensor_(free)(input);
 }
 
-static void THNN_(SpatialMaxPooling_updateGradInput_frame)(
+static void THNN_(SpatialDilatedMaxPooling_updateGradInput_frame)(
           real *gradInput_p,
           real *gradOutput_p,
           real *ind_p,
@@ -231,7 +231,7 @@ static void THNN_(SpatialMaxPooling_updateGradInput_frame)(
   }
 }
 
-void THNN_(SpatialMaxPooling_updateGradInput)(
+void THNN_(SpatialDilatedMaxPooling_updateGradInput)(
           THNNState *state,
           THTensor *input,
           THTensor *gradOutput,
@@ -287,7 +287,7 @@ void THNN_(SpatialMaxPooling_updateGradInput)(
   /* backprop */
   if (input->nDimension == 3)
   {
-    THNN_(SpatialMaxPooling_updateGradInput_frame)(gradInput_data, gradOutput_data,
+    THNN_(SpatialDilatedMaxPooling_updateGradInput_frame)(gradInput_data, gradOutput_data,
                                                  indices_data,
                                                  nslices,
                                                  iwidth, iheight,
@@ -300,7 +300,7 @@ void THNN_(SpatialMaxPooling_updateGradInput)(
 #pragma omp parallel for private(p)
     for (p = 0; p < nbatch; p++)
     {
-      THNN_(SpatialMaxPooling_updateGradInput_frame)(gradInput_data+p*nslices*iwidth*iheight, gradOutput_data+p*nslices*owidth*oheight,
+      THNN_(SpatialDilatedMaxPooling_updateGradInput_frame)(gradInput_data+p*nslices*iwidth*iheight, gradOutput_data+p*nslices*owidth*oheight,
                                                    indices_data+p*nslices*owidth*oheight,
                                                    nslices,
                                                    iwidth, iheight,
diff --git a/lib/THNN/generic/SpatialMaxPooling.c b/lib/THNN/generic/SpatialMaxPooling.c
index 3daef1d..e0fafb1 100644
--- a/lib/THNN/generic/SpatialMaxPooling.c
+++ b/lib/THNN/generic/SpatialMaxPooling.c
@@ -2,78 +2,6 @@
 #define TH_GENERIC_FILE "generic/SpatialMaxPooling.c"
 #else
 
-static void THNN_(SpatialMaxPooling_updateOutput_frame)(
-          real *input_p,
-          real *output_p,
-          real *ind_p,
-          long nslices,
-          long iwidth,
-          long iheight,
-          long owidth,
-          long oheight,
-          int kW,
-          int kH,
-          int dW,
-          int dH,
-          int padW,
-          int padH,
-          int dilationW,
-          int dilationH
-          )
-{
-  long k;
-#pragma omp parallel for private(k)
-  for (k = 0; k < nslices; k++)
-  {
-    /* loop over output */
-    long i, j;
-    real *ip = input_p   + k*iwidth*iheight;
-    for(i = 0; i < oheight; i++)
-    {
-      for(j = 0; j < owidth; j++)
-      {
-        long hstart = i * dH - padH;
-        long wstart = j * dW - padW;
-        long hend = fminf(hstart + (kH - 1) * dilationH + 1, iheight);
-        long wend = fminf(wstart + (kW - 1) * dilationW + 1, iwidth);
-        while(hstart < 0)
-          hstart += dilationH;
-        while(wstart < 0)
-          wstart += dilationW;
-
-        /* local pointers */
-        real *op = output_p  + k*owidth*oheight + i*owidth + j;
-        real *indp = ind_p   + k*owidth*oheight + i*owidth + j;
-
-        /* compute local max: */
-        long maxindex = -1;
-        real maxval = -THInf;
-        long tcntr = 0;
-        long x,y;
-        for(y = hstart; y < hend; y += dilationH)
-        {
-          for(x = wstart; x < wend; x += dilationW)
-          {
-            tcntr = y*iwidth + x;
-            real val = *(ip + tcntr);
-            if (val > maxval)
-            {
-              maxval = val;
-              maxindex = tcntr;
-            }
-          }
-        }
-
-        /* set output to local max */
-        *op = maxval;
-
-        /* store location of max */
-        *indp = maxindex + TH_INDEX_BASE;
-      }
-    }
-  }
-}
-
 void THNN_(SpatialMaxPooling_updateOutput)(
           THNNState *state,
           THTensor *input,
@@ -85,150 +13,12 @@ void THNN_(SpatialMaxPooling_updateOutput)(
           int dH,
           int padW,
           int padH,
-          int dilationW,
-          int dilationH,
           bool ceil_mode)
 {
-  int dimw = 2;
-  int dimh = 1;
-  long nbatch = 1;
-  long nslices;
-  long iheight;
-  long iwidth;
-  long oheight;
-  long owidth;
-  real *input_data;
-  real *output_data;
-  real *indices_data;
-
-
-  THArgCheck(input->nDimension == 3 || input->nDimension == 4 , 2, "3D or 4D (batch mode) tensor expected");
-
-  if (input->nDimension == 4)
-  {
-    nbatch = input->size[0];
-    dimw++;
-    dimh++;
-  }
-  THArgCheck(input->size[dimw] >= kW - padW && input->size[dimh] >= kH - padH, 2, "input image smaller than kernel size");
-  THArgCheck(kW/2 >= padW && kH/2 >= padH, 2, "pad should be smaller than half of kernel size");
-  
-  /* sizes */
-  nslices = input->size[dimh-1];
-  iheight = input->size[dimh];
-  iwidth = input->size[dimw];
-  if (ceil_mode)
-  {
-    oheight = (long)(ceil((float)(iheight - (dilationH * (kH - 1) + 1) + 2*padH) / dH)) + 1;
-    owidth  = (long)(ceil((float)(iwidth  - (dilationW * (kW - 1) + 1) + 2*padW) / dW)) + 1;
-  }
-  else
-  {
-    oheight = (long)(floor((float)(iheight - (dilationH * (kH - 1) + 1) + 2*padH) / dH)) + 1;
-    owidth  = (long)(floor((float)(iwidth  - (dilationW * (kW - 1) + 1) + 2*padW) / dW)) + 1;
-  }
-
-  if (owidth < 1 || oheight < 1)
-    THError("Given input size: (%dx%dx%d). Calculated output size: (%dx%dx%d). Output size is too small",
-            nslices,iheight,iwidth,nslices,oheight,owidth);
-
-  if (padW || padH)
-  {
-    // ensure that the last pooling starts inside the image
-    if ((oheight - 1)*dH >= iheight + padH)
-      --oheight;
-    if ((owidth  - 1)*dW >= iwidth  + padW)
-      --owidth;
-  }
-
-  /* get contiguous input */
-  input = THTensor_(newContiguous)(input);
-
-  /* resize output */
-  if (input->nDimension == 3)
-  {
-    THTensor_(resize3d)(output, nslices, oheight, owidth);
-    /* indices will contain the locations for each output point */
-    THTensor_(resize3d)(indices,  nslices, oheight, owidth);
-
-    input_data = THTensor_(data)(input);
-    output_data = THTensor_(data)(output);
-    indices_data = THTensor_(data)(indices);
-
-    THNN_(SpatialMaxPooling_updateOutput_frame)(input_data, output_data,
-                                              indices_data,
-                                              nslices,
-                                              iwidth, iheight,
-                                              owidth, oheight,
-                                              kW, kH, dW, dH,
-                                              padW, padH,
-                                              dilationW, dilationH
-                                              );
-  }
-  else
-  {
-    long p;
-
-    THTensor_(resize4d)(output, nbatch, nslices, oheight, owidth);
-    /* indices will contain the locations for each output point */
-    THTensor_(resize4d)(indices, nbatch, nslices, oheight, owidth);
-
-    input_data = THTensor_(data)(input);
-    output_data = THTensor_(data)(output);
-    indices_data = THTensor_(data)(indices);
-
-#pragma omp parallel for private(p)
-    for (p = 0; p < nbatch; p++)
-    {
-      THNN_(SpatialMaxPooling_updateOutput_frame)(input_data+p*nslices*iwidth*iheight, output_data+p*nslices*owidth*oheight,
-                                                indices_data+p*nslices*owidth*oheight,
-                                                nslices,
-                                                iwidth, iheight,
-                                                owidth, oheight,
-                                                kW, kH, dW, dH,
-                                                padW, padH,
-                                                dilationW, dilationH
-                                                );
-    }
-  }
-
-  /* cleanup */
-  THTensor_(free)(input);
-}
-
-static void THNN_(SpatialMaxPooling_updateGradInput_frame)(
-          real *gradInput_p,
-          real *gradOutput_p,
-          real *ind_p,
-          long nslices,
-          long iwidth,
-          long iheight,
-          long owidth,
-          long oheight,
-          int dW,
-          int dH)
-{
-  long k;
-#pragma omp parallel for private(k)
-  for (k = 0; k < nslices; k++)
-  {
-    real *gradInput_p_k = gradInput_p + k*iwidth*iheight;
-    real *gradOutput_p_k = gradOutput_p + k*owidth*oheight;
-    real *ind_p_k = ind_p + k*owidth*oheight;
-
-    /* calculate max points */
-    long i, j;
-    for(i = 0; i < oheight; i++)
-    {
-      for(j = 0; j < owidth; j++)
-      {
-        /* retrieve position of max */
-        long maxp = ind_p_k[i*owidth + j] - TH_INDEX_BASE;
-        /* update gradient */
-        gradInput_p_k[maxp] += gradOutput_p_k[i*owidth + j];
-      }
-    }
-  }
+  THNN_(SpatialDilatedMaxPooling_updateOutput)(
+      state, input, output, indices,
+      kW, kH, dW, dH, padW, padH, 1, 1, ceil_mode
+    );
 }
 
 void THNN_(SpatialMaxPooling_updateGradInput)(
@@ -243,74 +33,12 @@ void THNN_(SpatialMaxPooling_updateGradInput)(
           int dH,
           int padW,
           int padH,
-          int dilationW,
-          int dilationH,
           bool ceil_mode)
 {
-  int dimw = 2;
-  int dimh = 1;
-  long nbatch = 1;
-  int nslices;
-  int iheight;
-  int iwidth;
-  int oheight;
-  int owidth;
-  real *gradInput_data;
-  real *gradOutput_data;
-  real *indices_data;
-
-  /* get contiguous gradOutput */
-  gradOutput = THTensor_(newContiguous)(gradOutput);
-
-  /* resize */
-  THTensor_(resizeAs)(gradInput, input);
-  THTensor_(zero)(gradInput);
-
-  if (input->nDimension == 4) {
-    nbatch = input->size[0];
-    dimw++;
-    dimh++;
-  }
-
-  /* sizes */
-  nslices = input->size[dimh-1];
-  iheight = input->size[dimh];
-  iwidth = input->size[dimw];
-  oheight = gradOutput->size[dimh];
-  owidth = gradOutput->size[dimw];
-
-  /* get raw pointers */
-  gradInput_data = THTensor_(data)(gradInput);
-  gradOutput_data = THTensor_(data)(gradOutput);
-  indices_data = THTensor_(data)(indices);
-
-  /* backprop */
-  if (input->nDimension == 3)
-  {
-    THNN_(SpatialMaxPooling_updateGradInput_frame)(gradInput_data, gradOutput_data,
-                                                 indices_data,
-                                                 nslices,
-                                                 iwidth, iheight,
-                                                 owidth, oheight,
-                                                 dW, dH);
-  }
-  else
-  {
-    long p;
-#pragma omp parallel for private(p)
-    for (p = 0; p < nbatch; p++)
-    {
-      THNN_(SpatialMaxPooling_updateGradInput_frame)(gradInput_data+p*nslices*iwidth*iheight, gradOutput_data+p*nslices*owidth*oheight,
-                                                   indices_data+p*nslices*owidth*oheight,
-                                                   nslices,
-                                                   iwidth, iheight,
-                                                   owidth, oheight,
-                                                   dW, dH);
-    }
-  }
-
-  /* cleanup */
-  THTensor_(free)(gradOutput);
+  THNN_(SpatialDilatedMaxPooling_updateGradInput)(
+      state, input, gradOutput, gradInput, indices,
+      kW, kH, dW, dH, padW, padH, 1, 1, ceil_mode
+    );
 }
 
 #endif
diff --git a/lib/THNN/generic/SpatialUpSamplingNearest.c b/lib/THNN/generic/SpatialUpSamplingNearest.c
index 7ef093c..b67c68d 100644
--- a/lib/THNN/generic/SpatialUpSamplingNearest.c
+++ b/lib/THNN/generic/SpatialUpSamplingNearest.c
@@ -14,7 +14,7 @@ void THNN_(SpatialUpSamplingNearest_updateOutput)(
   int yDim = input->nDimension-1;
 
   // dims
-  int idim = input->nDimension;  // Gauranteed to be between 3 and 5
+  int idim = input->nDimension;  // Guaranteed to be between 3 and 5
   int osz0 = output->size[0];
   int osz1 = output->size[1];
   int osz2 = output->size[2];
@@ -80,7 +80,7 @@ void THNN_(SpatialUpSamplingNearest_updateGradInput)(
   int yDim = gradInput->nDimension-1;
 
   // dims
-  int idim = gradInput->nDimension;  // Gauranteed to be between 3 and 5
+  int idim = gradInput->nDimension;  // Guaranteed to be between 3 and 5
   int isz0 = gradInput->size[0];
   int isz1 = gradInput->size[1];
   int isz2 = gradInput->size[2];
diff --git a/lib/THNN/generic/THNN.h b/lib/THNN/generic/THNN.h
index 7ad6f70..0f2149a 100644
--- a/lib/THNN/generic/THNN.h
+++ b/lib/THNN/generic/THNN.h
@@ -25,6 +25,21 @@ TH_API void THNN_(AbsCriterion_updateGradInput)(
           THTensor *gradInput,         // [OUT] gradient w.r.t. input
           bool sizeAverage);           // if true, the gradient will be normalized by batch size
 
+TH_API void THNN_(BCECriterion_updateOutput)(
+          THNNState *state,
+          THTensor *input,
+          THTensor *target,
+          THTensor *output,
+          bool sizeAverage,
+	  THTensor *weights);
+TH_API void THNN_(BCECriterion_updateGradInput)(
+          THNNState *state,
+          THTensor *input,
+          THTensor *target,
+          THTensor *gradInput,
+          bool sizeAverage,
+	  THTensor *weights);
+
 TH_API void THNN_(ClassNLLCriterion_updateOutput)(
           THNNState *state,            // library's state
           THTensor *input,             // input tensor (1D/2D)
@@ -472,6 +487,7 @@ TH_API void THNN_(Threshold_updateGradInput)(
           THTensor *gradOutput,
           THTensor *gradInput,
           real threshold,
+          real val,
           bool inplace);
 
 TH_API void THNN_(TemporalConvolution_updateOutput)(
@@ -840,7 +856,6 @@ TH_API void THNN_(SpatialMaxPooling_updateOutput)(
           int kW, int kH,
           int dW, int dH,
           int padW, int padH,
-          int dilationW, int dilationH,
           bool ceil_mode);
 TH_API void THNN_(SpatialMaxPooling_updateGradInput)(
           THNNState *state,
@@ -851,6 +866,27 @@ TH_API void THNN_(SpatialMaxPooling_updateGradInput)(
           int kW, int kH,
           int dW, int dH,
           int padW, int padH,
+          bool ceil_mode);
+
+TH_API void THNN_(SpatialDilatedMaxPooling_updateOutput)(
+          THNNState *state,
+          THTensor *input,
+          THTensor *output,
+          THTensor *indices,
+          int kW, int kH,
+          int dW, int dH,
+          int padW, int padH,
+          int dilationW, int dilationH,
+          bool ceil_mode);
+TH_API void THNN_(SpatialDilatedMaxPooling_updateGradInput)(
+          THNNState *state,
+          THTensor *input,
+          THTensor *gradOutput,
+          THTensor *gradInput,
+          THTensor *indices,
+          int kW, int kH,
+          int dW, int dH,
+          int padW, int padH,
           int dilationW, int dilationH,
           bool ceil_mode);
 
@@ -1101,6 +1137,26 @@ TH_API void THNN_(VolumetricMaxPooling_updateGradInput)(
           int dT, int dW, int dH,
           int pT, int pW, int pH);
 
+TH_API void THNN_(VolumetricDilatedMaxPooling_updateOutput)(
+          THNNState *state,
+          THTensor *input,
+          THTensor *output,
+          THTensor *indices,
+          int kT, int kW, int kH,
+          int dT, int dW, int dH,
+          int pT, int pW, int pH,
+          int dilationT, int dilationW, int dilationH,
+          bool ceilMode);
+TH_API void THNN_(VolumetricDilatedMaxPooling_updateGradInput)(
+          THNNState *state,
+          THTensor *input,
+          THTensor *gradOutput,
+          THTensor *gradInput,
+          THTensor *indices,
+          int dT, int dW, int dH,
+          int pT, int pW, int pH,
+          int dilationT, int dilationW, int dilationH);
+
 TH_API void THNN_(VolumetricMaxUnpooling_updateOutput)(
           THNNState *state,
           THTensor *input,
diff --git a/lib/THNN/generic/Threshold.c b/lib/THNN/generic/Threshold.c
index ac00360..54310a0 100644
--- a/lib/THNN/generic/Threshold.c
+++ b/lib/THNN/generic/Threshold.c
@@ -33,6 +33,7 @@ void THNN_(Threshold_updateGradInput)(
           THTensor *gradOutput,
           THTensor *gradInput,
           real threshold,
+          real val,
           bool inplace)
 {
   if (inplace)
diff --git a/lib/THNN/generic/VolumetricMaxPooling.c b/lib/THNN/generic/VolumetricDilatedMaxPooling.c
similarity index 76%
copy from lib/THNN/generic/VolumetricMaxPooling.c
copy to lib/THNN/generic/VolumetricDilatedMaxPooling.c
index 053c02c..0db41ae 100644
--- a/lib/THNN/generic/VolumetricMaxPooling.c
+++ b/lib/THNN/generic/VolumetricDilatedMaxPooling.c
@@ -1,8 +1,8 @@
 #ifndef TH_GENERIC_FILE
-#define TH_GENERIC_FILE "generic/VolumetricMaxPooling.c"
+#define TH_GENERIC_FILE "generic/VolumetricDilatedMaxPooling.c"
 #else
 
-static void THNN_(VolumetricMaxPooling_updateOutput_frame)(
+static void THNN_(VolumetricDilatedMaxPooling_updateOutput_frame)(
           real *input_p,
           real *output_p,
           real *indz_p,
@@ -21,7 +21,10 @@ static void THNN_(VolumetricMaxPooling_updateOutput_frame)(
           int dH,
           int pT,
           int pW,
-          int pH)
+          int pH,
+          int dilationT,
+          int dilationW,
+          int dilationH)
 {
   long k;
 #pragma omp parallel for private(k)
@@ -40,15 +43,18 @@ static void THNN_(VolumetricMaxPooling_updateOutput_frame)(
           long start_t = ti * dT - pT;
           long start_h = i * dH - pH;
           long start_w = j * dW - pW;
-
+          
           long kernel_t = fminf(kT, kT + start_t);
           long kernel_h = fminf(kH, kH + start_h);
           long kernel_w = fminf(kW, kW + start_w);
 
-          start_t = fmaxf(start_t, 0);
-          start_h = fmaxf(start_h, 0);
-          start_w = fmaxf(start_w, 0);
-
+          while(start_t < 0)
+            start_t += dilationT;
+          while(start_h < 0)
+            start_h += dilationH;
+          while(start_w < 0)
+            start_w += dilationW;
+          
           real *ip = input_p + k * itime * iwidth * iheight
             + start_t * iwidth * iheight + start_h * iwidth + start_w;
           real *op = output_p + k * otime * owidth * oheight
@@ -67,9 +73,9 @@ static void THNN_(VolumetricMaxPooling_updateOutput_frame)(
             {
               for (x = 0; x < kernel_w; x++)
               {
-                if ((start_t + z < itime) && (start_h + y < iheight) && (start_w + x < iwidth))
+                if ((start_t + z * dilationT < itime) && (start_h + y * dilationH < iheight) && (start_w + x * dilationW < iwidth))
                 {
-                  real val = *(ip + z * iwidth * iheight + y * iwidth + x);
+                  real val = *(ip + z * dilationT * iwidth * iheight + y * dilationH * iwidth + x * dilationW);
                   if (val > maxval)
                   {
                     maxval = val;
@@ -97,7 +103,7 @@ static void THNN_(VolumetricMaxPooling_updateOutput_frame)(
   }
 }
 
-void THNN_(VolumetricMaxPooling_updateOutput)(
+void THNN_(VolumetricDilatedMaxPooling_updateOutput)(
           THNNState *state,
           THTensor *input,
           THTensor *output,
@@ -111,6 +117,9 @@ void THNN_(VolumetricMaxPooling_updateOutput)(
           int pT,
           int pW,
           int pH,
+          int dilationT,
+          int dilationW,
+          int dilationH,
           bool ceilMode)
 {
   long nslices;
@@ -156,17 +165,21 @@ void THNN_(VolumetricMaxPooling_updateOutput)(
   iwidth  = input->size[dimw];
   if (ceilMode)
   {
-    otime   = (int)(ceil((float)(itime   - kT + 2 * pT) / dT) + 1);
-    oheight = (int)(ceil((float)(iheight - kH + 2 * pH) / dH) + 1);
-    owidth  = (int)(ceil((float)(iwidth  - kW + 2 * pW) / dW) + 1);
+    otime = (int)(ceil((float)(itime - (dilationT * (kT - 1) + 1) + 2*pT) / dT)) + 1;
+    oheight = (int)(ceil((float)(iheight - (dilationH * (kH - 1) + 1) + 2*pH) / dH)) + 1;
+    owidth  = (int)(ceil((float)(iwidth  - (dilationW * (kW - 1) + 1) + 2*pW) / dW)) + 1;
   }
   else
   {
-    otime   = (int)(floor((float)(itime   - kT + 2 * pT) / dT) + 1);
-    oheight = (int)(floor((float)(iheight - kH + 2 * pH) / dH) + 1);
-    owidth  = (int)(floor((float)(iwidth  - kW + 2 * pW) / dW) + 1);
+    otime = (int)(floor((float)(itime - (dilationT * (kT - 1) + 1) + 2*pT) / dT)) + 1;
+    oheight = (int)(floor((float)(iheight - (dilationH * (kH - 1) + 1) + 2*pH) / dH)) + 1;
+    owidth  = (int)(floor((float)(iwidth  - (dilationW * (kW - 1) + 1) + 2*pW) / dW)) + 1;
   }
 
+  if (otime < 1 || owidth < 1 || oheight < 1)
+    THError("Given input size: (%dx%dx%dx%d). Calculated output size: (%dx%dx%dx%d). Output size is too small",
+            nslices,itime,iheight,iwidth,nslices,otime,oheight,owidth);
+
   if (pT || pW || pH)
   {
     // ensure that the last pooling starts inside the image
@@ -192,7 +205,7 @@ void THNN_(VolumetricMaxPooling_updateOutput)(
     output_data = THTensor_(data)(output);
     indices_data = THTensor_(data)(indices);
 
-    THNN_(VolumetricMaxPooling_updateOutput_frame)(
+    THNN_(VolumetricDilatedMaxPooling_updateOutput_frame)(
       input_data, output_data,
       indices_data,
       nslices,
@@ -200,7 +213,8 @@ void THNN_(VolumetricMaxPooling_updateOutput)(
       otime, owidth, oheight,
       kT, kW, kH,
       dT, dW, dH,
-      pT, pW, pH
+      pT, pW, pH,
+      dilationT, dilationW, dilationH
     );
   }
   else /* batch mode */
@@ -223,7 +237,7 @@ void THNN_(VolumetricMaxPooling_updateOutput)(
 #pragma omp parallel for private(p)
     for (p=0; p < nBatch; p++)
     {
-      THNN_(VolumetricMaxPooling_updateOutput_frame)(
+      THNN_(VolumetricDilatedMaxPooling_updateOutput_frame)(
         input_data   + p * istride,
         output_data  + p * ostride,
         indices_data + p * ostride,
@@ -232,7 +246,8 @@ void THNN_(VolumetricMaxPooling_updateOutput)(
         otime, owidth, oheight,
         kT, kW, kH,
         dT, dW, dH,
-        pT, pW, pH
+        pT, pW, pH,
+        dilationT, dilationW, dilationH
       );
     }
   }
@@ -241,7 +256,7 @@ void THNN_(VolumetricMaxPooling_updateOutput)(
   THTensor_(free)(input);
 }
 
-static void THNN_(VolumetricMaxPooling_updateGradInput_frame)(
+static void THNN_(VolumetricDilatedMaxPooling_updateGradInput_frame)(
           real *gradInput_p,
           real *gradOutput_p,
           real *indz_p,
@@ -257,7 +272,10 @@ static void THNN_(VolumetricMaxPooling_updateGradInput_frame)(
           int dH,
           int pT,
           int pW,
-          int pH)
+          int pH,
+          int dilationT,
+          int dilationW,
+          int dilationH)
 {
   long k;
 #pragma omp parallel for private(k)
@@ -277,9 +295,9 @@ static void THNN_(VolumetricMaxPooling_updateGradInput_frame)(
         {
           /* retrieve position of max */
           real * indzp = &indz_p_k[ti * oheight * owidth + i * owidth + j];
-          long maxti = ((unsigned char*)(indzp))[0] + ti * dT - pT;
-          long maxi  = ((unsigned char*)(indzp))[1] + i * dH - pH;
-          long maxj  = ((unsigned char*)(indzp))[2] + j * dW - pW;
+          long maxti = ((unsigned char*)(indzp))[0] * dilationT + ti * dT - pT;
+          long maxi  = ((unsigned char*)(indzp))[1] * dilationH + i * dH - pH;
+          long maxj  = ((unsigned char*)(indzp))[2] * dilationW + j * dW - pW;
 
           /* update gradient */
           gradInput_p_k[maxti * iheight * iwidth + maxi * iwidth + maxj] +=
@@ -290,7 +308,7 @@ static void THNN_(VolumetricMaxPooling_updateGradInput_frame)(
   }
 }
 
-void THNN_(VolumetricMaxPooling_updateGradInput)(
+void THNN_(VolumetricDilatedMaxPooling_updateGradInput)(
           THNNState *state,
           THTensor *input,
           THTensor *gradOutput,
@@ -301,7 +319,10 @@ void THNN_(VolumetricMaxPooling_updateGradInput)(
           int dH,
           int pT,
           int pW,
-          int pH)
+          int pH,
+          int dilationT,
+          int dilationW,
+          int dilationH)
 {
   int nslices;
   int itime;
@@ -351,14 +372,15 @@ void THNN_(VolumetricMaxPooling_updateGradInput)(
   /* backprop */
   if (input->nDimension == 4) /* non-batch mode*/
   {
-    THNN_(VolumetricMaxPooling_updateGradInput_frame)(
+    THNN_(VolumetricDilatedMaxPooling_updateGradInput_frame)(
       gradInput_data, gradOutput_data,
       indices_data,
       nslices,
       itime, iwidth, iheight,
       otime, owidth, oheight,
       dT, dW, dH,
-      pT, pW, pH
+      pT, pW, pH,
+      dilationT, dilationW, dilationH
     );
   }
   else /* batch mode */
@@ -372,7 +394,7 @@ void THNN_(VolumetricMaxPooling_updateGradInput)(
 #pragma omp parallel for private(p)
     for (p = 0; p < nBatch; p++)
     {
-      THNN_(VolumetricMaxPooling_updateGradInput_frame)(
+      THNN_(VolumetricDilatedMaxPooling_updateGradInput_frame)(
         gradInput_data + p * istride,
         gradOutput_data + p * ostride,
         indices_data + p * ostride,
@@ -380,7 +402,8 @@ void THNN_(VolumetricMaxPooling_updateGradInput)(
         itime, iwidth, iheight,
         otime, owidth, oheight,
         dT, dW, dH,
-        pT, pW, pH
+        pT, pW, pH,
+        dilationT, dilationW, dilationH
       );
     }
   }
diff --git a/lib/THNN/generic/VolumetricMaxPooling.c b/lib/THNN/generic/VolumetricMaxPooling.c
index 053c02c..dc376e6 100644
--- a/lib/THNN/generic/VolumetricMaxPooling.c
+++ b/lib/THNN/generic/VolumetricMaxPooling.c
@@ -2,101 +2,6 @@
 #define TH_GENERIC_FILE "generic/VolumetricMaxPooling.c"
 #else
 
-static void THNN_(VolumetricMaxPooling_updateOutput_frame)(
-          real *input_p,
-          real *output_p,
-          real *indz_p,
-          long nslices,
-          long itime,
-          long iwidth,
-          long iheight,
-          long otime,
-          long owidth,
-          long oheight,
-          int kT,
-          int kW,
-          int kH,
-          int dT,
-          int dW,
-          int dH,
-          int pT,
-          int pW,
-          int pH)
-{
-  long k;
-#pragma omp parallel for private(k)
-  for (k = 0; k < nslices; k++)
-  {
-    /* loop over output */
-    long i, j, ti;
-    for (ti = 0; ti < otime; ti++)
-    {
-      for (i = 0; i < oheight; i++)
-      {
-        for (j = 0; j < owidth; j++)
-        {
-          /* local pointers */
-
-          long start_t = ti * dT - pT;
-          long start_h = i * dH - pH;
-          long start_w = j * dW - pW;
-
-          long kernel_t = fminf(kT, kT + start_t);
-          long kernel_h = fminf(kH, kH + start_h);
-          long kernel_w = fminf(kW, kW + start_w);
-
-          start_t = fmaxf(start_t, 0);
-          start_h = fmaxf(start_h, 0);
-          start_w = fmaxf(start_w, 0);
-
-          real *ip = input_p + k * itime * iwidth * iheight
-            + start_t * iwidth * iheight + start_h * iwidth + start_w;
-          real *op = output_p + k * otime * owidth * oheight
-            + ti * owidth * oheight + i * owidth + j;
-          real *indzp = indz_p + k * otime * owidth * oheight
-            + ti * owidth * oheight + i * owidth + j;
-
-          /* compute local max: */
-          real maxval = -THInf;
-          int x,y,z;
-          int mx, my, mz;
-
-          for (z = 0; z < kernel_t; z++)
-          {
-            for (y = 0; y < kernel_h; y++)
-            {
-              for (x = 0; x < kernel_w; x++)
-              {
-                if ((start_t + z < itime) && (start_h + y < iheight) && (start_w + x < iwidth))
-                {
-                  real val = *(ip + z * iwidth * iheight + y * iwidth + x);
-                  if (val > maxval)
-                  {
-                    maxval = val;
-                    // Store indices w.r.t the kernel dimension
-                    mz = z + (kT - kernel_t);
-                    my = y + (kH - kernel_h);
-                    mx = x + (kW - kernel_w);
-                  }
-                }
-              }
-            }
-          }
-
-          // set max values
-          ((unsigned char*)(indzp))[0] = mz;
-          ((unsigned char*)(indzp))[1] = my;
-          ((unsigned char*)(indzp))[2] = mx;
-          ((unsigned char*)(indzp))[3] = 0;
-
-          /* set output to local max */
-          *op = maxval;
-        }
-      }
-    }
-  }
-}
-
 void THNN_(VolumetricMaxPooling_updateOutput)(
           THNNState *state,
           THTensor *input,
@@ -113,181 +18,10 @@ void THNN_(VolumetricMaxPooling_updateOutput)(
           int pH,
           bool ceilMode)
 {
-  long nslices;
-  long itime;
-  long iheight;
-  long iwidth;
-  long otime;
-  long oheight;
-  long owidth;
-  real *input_data;
-  real *output_data;
-  real *indices_data;
-
-  THArgCheck(input->nDimension == 4 || input->nDimension == 5, 2,
-    "4D or 5D (batch-mode) tensor expected"
-  );
-
-  int dimN = 0;
-  int dimt = 1;
-  int dimh = 2;
-  int dimw = 3;
-
-  if (input->nDimension == 5)
-  {
-    dimN++;
-    dimt++;
-    dimh++;
-    dimw++;
-  }
-
-  THArgCheck(input->size[dimw] >= kW && input->size[dimh] >= kH && input->size[dimt] >= kT, 2,
-    "input image smaller than kernel size"
-  );
-
-  THArgCheck(kT/2 >= pT && kW/2 >= pW && kH/2 >= pH, 2,
-    "pad should be smaller than half of kernel size"
-  );
-
-  /* sizes */
-  nslices = input->size[dimN];
-  itime   = input->size[dimt];
-  iheight = input->size[dimh];
-  iwidth  = input->size[dimw];
-  if (ceilMode)
-  {
-    otime   = (int)(ceil((float)(itime   - kT + 2 * pT) / dT) + 1);
-    oheight = (int)(ceil((float)(iheight - kH + 2 * pH) / dH) + 1);
-    owidth  = (int)(ceil((float)(iwidth  - kW + 2 * pW) / dW) + 1);
-  }
-  else
-  {
-    otime   = (int)(floor((float)(itime   - kT + 2 * pT) / dT) + 1);
-    oheight = (int)(floor((float)(iheight - kH + 2 * pH) / dH) + 1);
-    owidth  = (int)(floor((float)(iwidth  - kW + 2 * pW) / dW) + 1);
-  }
-
-  if (pT || pW || pH)
-  {
-    // ensure that the last pooling starts inside the image
-    if ((otime - 1)*dT >= itime + pT)
-      --otime;
-    if ((oheight - 1)*dH >= iheight + pH)
-      --oheight;
-    if ((owidth  - 1)*dW >= iwidth  + pW)
-      --owidth;
-  }
-
-  /* get contiguous input */
-  input = THTensor_(newContiguous)(input);
-
-  if (input->nDimension == 4) /* non-batch mode */
-  {
-    /* resize output */
-    THTensor_(resize4d)(output, nslices, otime, oheight, owidth);
-    /* indices will contain ti,i,j uchar locations packed into float/double */
-    THTensor_(resize4d)(indices, nslices, otime, oheight, owidth);
-
-    input_data = THTensor_(data)(input);
-    output_data = THTensor_(data)(output);
-    indices_data = THTensor_(data)(indices);
-
-    THNN_(VolumetricMaxPooling_updateOutput_frame)(
-      input_data, output_data,
-      indices_data,
-      nslices,
-      itime, iwidth, iheight,
-      otime, owidth, oheight,
-      kT, kW, kH,
-      dT, dW, dH,
-      pT, pW, pH
-    );
-  }
-  else /* batch mode */
-  {
-    long p;
-    long nBatch = input->size[0];
-
-    long istride = nslices * itime * iwidth * iheight;
-    long ostride = nslices * otime * owidth * oheight;
-
-    /* resize output */
-    THTensor_(resize5d)(output, nBatch, nslices, otime, oheight, owidth);
-    /* indices will contain ti,i,j locations for each output point */
-    THTensor_(resize5d)(indices, nBatch, nslices, otime, oheight, owidth);
-
-    input_data = THTensor_(data)(input);
-    output_data = THTensor_(data)(output);
-    indices_data = THTensor_(data)(indices);
-
-#pragma omp parallel for private(p)
-    for (p=0; p < nBatch; p++)
-    {
-      THNN_(VolumetricMaxPooling_updateOutput_frame)(
-        input_data   + p * istride,
-        output_data  + p * ostride,
-        indices_data + p * ostride,
-        nslices,
-        itime, iwidth, iheight,
-        otime, owidth, oheight,
-        kT, kW, kH,
-        dT, dW, dH,
-        pT, pW, pH
-      );
-    }
-  }
-
-  /* cleanup */
-  THTensor_(free)(input);
-}
-
-static void THNN_(VolumetricMaxPooling_updateGradInput_frame)(
-          real *gradInput_p,
-          real *gradOutput_p,
-          real *indz_p,
-          long nslices,
-          long itime,
-          long iwidth,
-          long iheight,
-          long otime,
-          long owidth,
-          long oheight,
-          int dT,
-          int dW,
-          int dH,
-          int pT,
-          int pW,
-          int pH)
-{
-  long k;
-#pragma omp parallel for private(k)
-  for (k = 0; k < nslices; k++)
-  {
-    real *gradInput_p_k  = gradInput_p  + k * itime * iwidth * iheight;
-    real *gradOutput_p_k = gradOutput_p + k * otime * owidth * oheight;
-    real *indz_p_k = indz_p + k * otime * owidth * oheight;
-
-    /* calculate max points */
-    long ti, i, j;
-    for (ti = 0; ti < otime; ti++)
-    {
-      for (i = 0; i < oheight; i++)
-      {
-        for (j = 0; j < owidth; j++)
-        {
-          /* retrieve position of max */
-          real * indzp = &indz_p_k[ti * oheight * owidth + i * owidth + j];
-          long maxti = ((unsigned char*)(indzp))[0] + ti * dT - pT;
-          long maxi  = ((unsigned char*)(indzp))[1] + i * dH - pH;
-          long maxj  = ((unsigned char*)(indzp))[2] + j * dW - pW;
-
-          /* update gradient */
-          gradInput_p_k[maxti * iheight * iwidth + maxi * iwidth + maxj] +=
-            gradOutput_p_k[ti * oheight * owidth + i * owidth + j];
-        }
-      }
-    }
-  }
+  THNN_(VolumetricDilatedMaxPooling_updateOutput)(
+          state, input, output, indices,
+          kT, kW, kH, dT, dW, dH,
+          pT, pW, pH, 1, 1, 1, ceilMode);
 }
 
 void THNN_(VolumetricMaxPooling_updateGradInput)(
@@ -303,90 +37,9 @@ void THNN_(VolumetricMaxPooling_updateGradInput)(
           int pW,
           int pH)
 {
-  int nslices;
-  int itime;
-  int iheight;
-  int iwidth;
-  int otime;
-  int oheight;
-  int owidth;
-  real *gradInput_data;
-  real *gradOutput_data;
-  real *indices_data;
-
-  int dimN = 0;
-  int dimt = 1;
-  int dimh = 2;
-  int dimw = 3;
-
-  /* get contiguous gradOutput */
-  gradOutput = THTensor_(newContiguous)(gradOutput);
-
-  /* resize */
-  THTensor_(resizeAs)(gradInput, input);
-  THTensor_(zero)(gradInput);
-
-  if (input->nDimension == 5)
-  {
-    dimN++;
-    dimt++;
-    dimh++;
-    dimw++;
-  }
-
-  /* sizes */
-  nslices = input->size[dimN];
-  itime = input->size[dimt];
-  iheight = input->size[dimh];
-  iwidth = input->size[dimw];
-  otime = gradOutput->size[dimt];
-  oheight = gradOutput->size[dimh];
-  owidth = gradOutput->size[dimw];
-
-  /* get raw pointers */
-  gradInput_data = THTensor_(data)(gradInput);
-  gradOutput_data = THTensor_(data)(gradOutput);
-  indices_data = THTensor_(data)(indices);
-
-  /* backprop */
-  if (input->nDimension == 4) /* non-batch mode*/
-  {
-    THNN_(VolumetricMaxPooling_updateGradInput_frame)(
-      gradInput_data, gradOutput_data,
-      indices_data,
-      nslices,
-      itime, iwidth, iheight,
-      otime, owidth, oheight,
-      dT, dW, dH,
-      pT, pW, pH
-    );
-  }
-  else /* batch mode */
-  {
-    long p;
-    long nBatch = input->size[0];
-
-    long istride = nslices * itime * iwidth * iheight;
-    long ostride = nslices * otime * owidth * oheight;
-
-#pragma omp parallel for private(p)
-    for (p = 0; p < nBatch; p++)
-    {
-      THNN_(VolumetricMaxPooling_updateGradInput_frame)(
-        gradInput_data + p * istride,
-        gradOutput_data + p * ostride,
-        indices_data + p * ostride,
-        nslices,
-        itime, iwidth, iheight,
-        otime, owidth, oheight,
-        dT, dW, dH,
-        pT, pW, pH
-      );
-    }
-  }
-
-  /* cleanup */
-  THTensor_(free)(gradOutput);
+  THNN_(VolumetricDilatedMaxPooling_updateGradInput)(
+          state, input, gradOutput, gradInput, indices,
+          dT, dW, dH, pT, pW, pH, 1, 1, 1);
 }
 
 #endif
diff --git a/lib/THNN/init.c b/lib/THNN/init.c
index 739706c..b4218cb 100644
--- a/lib/THNN/init.c
+++ b/lib/THNN/init.c
@@ -10,6 +10,9 @@
 #include "generic/AbsCriterion.c"
 #include "THGenerateFloatTypes.h"
 
+#include "generic/BCECriterion.c"
+#include "THGenerateFloatTypes.h"
+
 #include "generic/ClassNLLCriterion.c"
 #include "THGenerateFloatTypes.h"
 
@@ -139,6 +142,9 @@
 #include "generic/SpatialMaxPooling.c"
 #include "THGenerateFloatTypes.h"
 
+#include "generic/SpatialDilatedMaxPooling.c"
+#include "THGenerateFloatTypes.h"
+
 #include "generic/SpatialMaxUnpooling.c"
 #include "THGenerateFloatTypes.h"
 
@@ -169,6 +175,9 @@
 #include "generic/VolumetricMaxPooling.c"
 #include "THGenerateFloatTypes.h"
 
+#include "generic/VolumetricDilatedMaxPooling.c"
+#include "THGenerateFloatTypes.h"
+
 #include "generic/VolumetricMaxUnpooling.c"
 #include "THGenerateFloatTypes.h"
 
diff --git a/test.lua b/test.lua
index e288e25..9098b46 100644
--- a/test.lua
+++ b/test.lua
@@ -125,8 +125,9 @@ function nntest.CMul()
    local ini = math.random(3,5)
    local inj = math.random(3,5)
    local ink = math.random(3,5)
+   local inl = math.random(3,5)
    local input = torch.Tensor(ini,inj,ink):zero()
-   local module = nn.CMul(ini, inj, ink)
+   local module = nn.CMul(1, ini, inj, ink, 1)
 
    -- 1D
    local err = jac.testJacobian(module,input)
@@ -144,8 +145,7 @@ function nntest.CMul()
    end
 
    -- 2D
-   local nframe = math.random(50,70)
-   local nframe = 5
+   local nframe = math.random(3,14)
    local input = torch.randn(nframe, ini,inj,ink)
    local output = module:forward(input)
    local output2 = torch.cmul(input, module.weight:view(1,ini,inj,ink):expandAs(input))
@@ -168,6 +168,27 @@ function nntest.CMul()
    mytester:assertTensorEq(gradWeight, module.gradWeight, 0.000001, 'CMul accGradParameters 2D err')
    mytester:assert(module.weight:isSameSizeAs(module.gradWeight), 'CMul gradWeight size err')
 
+   -- Expansion
+   input = torch.randn(nframe, ini,inj,ink,inl)
+   output = module:forward(input)
+   output2 = torch.cmul(input, module.weight:expandAs(input))
+   mytester:assertTensorEq(output2, output, 0.000001, 'CMul forward expand err')
+
+   module:zeroGradParameters()
+   gradWeight:zero()
+   gradInput = module:backward(input, output)
+   gradInput2 = gradInput:clone():zero()
+   gradInput2:addcmul(1, module.weight:expandAs(output), output)
+   mytester:assertTensorEq(gradInput2, gradInput, 0.000001, 'CMul updateGradInput expansion err')
+   mytester:assert(gradInput:isSameSizeAs(input), 'CMul gradInput expand size err')
+
+   for i=1,nframe do
+      -- 4 is the [non-batch] singleton dim
+      gradWeight:add(torch.cmul(input[i], output[i]):sum(4))
+   end
+   mytester:assertTensorEq(gradWeight:sum(5), module.gradWeight, 0.000001, 'CMul accGradParameters expand err')
+   mytester:assert(module.weight:isSameSizeAs(module.gradWeight), 'CMul accGradParameters expand size err')
+
    input:zero()
 
    local err = jac.testJacobian(module,input)
@@ -360,6 +381,19 @@ function nntest.HardTanh()
    local ferr, berr = jac.testIO(module, input)
    mytester:asserteq(ferr, 0, torch.typename(module) .. ' - i/o forward err ')
    mytester:asserteq(berr, 0, torch.typename(module) .. ' - i/o backward err ')
+
+   -- test inclusive bounds -- HardTahn(1,inf) should behave like Threshold(1)
+   local input = torch.Tensor({1})
+   local gradOutput = torch.Tensor({1})
+   local gradOutputClone = gradOutput:clone()
+   local module = nn.HardTanh(1, math.huge, true)
+   local tanhGradInput = module:backward(input, gradOutput)
+
+   local input = input:clone()
+   local gradOutput = gradOutputClone
+   local module  = nn.Threshold(1, 0, true)
+   local threshGradInput = module:backward(input, gradOutput)
+   mytester:assertTensorEq(tanhGradInput, threshGradInput, 0.000001, 'HardTanh gradInput')
 end
 
 function nntest.Clamp()
@@ -2296,6 +2330,21 @@ function nntest.SpatialConvolution()
    module.gradBias = torch.Tensor(module.nOutputPlane):zero()
    module:reset()
    jacTests(module)
+
+   local output = module:forward(input):clone()
+   local gradOutput = output:clone():normal()
+   local gradInput = module:forward(input, gradOutput):clone()
+   local bigWeight = module.weight.new(module.weight:nElement() * 4):fill(0/0) -- fill with nans
+   local newWeight = bigWeight:narrow(1, module.weight:nElement() * 3, module.weight:nElement())
+   newWeight = newWeight:viewAs(module.weight):copy(module.weight)
+   module.weight = newWeight
+   local newOutput = module:forward(input)
+   local newGradInput = module:forward(input, gradOutput)
+   mytester:asserteq((newOutput - output):abs():max(), 0,
+      torch.typename(module) .. ' forward failure case in a getParameters setting ')
+   mytester:asserteq((newGradInput - gradInput):abs():max(), 0,
+      torch.typename(module) .. ' backward failure case in a getParameters setting ')
+
 end
 
 function nntest.SpatialConvolutionMM()
@@ -3679,6 +3728,48 @@ function nntest.TemporalConvolution()
    mytester:assertTensorEq(inputGrad:select(1,2), inputGrad1D, 0.000001, 'error on 2D vs 1D backward)')
 end
 
+function nntest.TemporalDynamicKMaxPooling()
+   local features = math.random(5,10)
+   local seqLen = math.random(6,9)
+   local minK = math.random(3,6)
+   local factor = math.random(1,100)*0.01
+   local nBatchFrame = math.random(2,4)
+   local module = nn.TemporalDynamicKMaxPooling(minK, factor)
+
+   -- 1D
+   local input = torch.Tensor(seqLen, features)
+   local err = jac.testJacobian(module, input)
+   mytester:assertlt(err, precision, 'error on state ')
+
+   local ferr, berr = jac.testIO(module, input)
+   mytester:asserteq(0, ferr, torch.typename(module) .. ' - i/o forward err ')
+   mytester:asserteq(0, berr, torch.typename(module) .. ' - i/o backward err ')
+
+   -- 2D
+   local input = torch.Tensor(nBatchFrame, seqLen, features)
+   local err = jac.testJacobian(module, input)
+   mytester:assertlt(err, precision, 'error on state ')
+
+   local ferr, berr = jac.testIO(module, input)
+   mytester:asserteq(0, ferr, torch.typename(module) .. ' - i/o forward err ')
+   mytester:asserteq(0, berr, torch.typename(module) .. ' - i/o backward err ')
+
+   -- 2D matches 1D
+   local output = module:forward(input):clone()
+   local outputGrad = torch.randn(output:size())
+   local inputGrad = module:backward(input, outputGrad):clone()
+
+   local input1D = input:select(1, 2)
+   local output1D = module:forward(input1D)
+   local outputGrad1D = outputGrad:select(1, 2)
+   local inputGrad1D = module:backward(input1D, outputGrad1D)
+
+   mytester:assertTensorEq(output:select(1,2), output1D, 0.000001, 'error on 2D vs 1D forward)')
+   mytester:assertTensorEq(inputGrad:select(1,2), inputGrad1D, 0.000001, 'error on 2D vs 1D backward)')
+
+
+end
+
 function nntest.TemporalSubSampling()
    local from = math.random(1,5)
    local ki = math.random(1,6)
@@ -4145,6 +4236,55 @@ function nntest.VolumetricMaxPooling()
    mytester:asserteq(berr, 0, torch.typename(module) .. ' - i/o backward err (Batch) ')
 end
 
+function nntest.VolumetricDilatedMaxPooling()
+   for _,ceil_mode in pairs({true,false}) do
+      local from = math.random(2,3)
+      local kt = math.random(3,4)
+      local ki = math.random(3,4)
+      local kj = math.random(3,4)
+      local st = math.random(2,3)
+      local si = math.random(2,3)
+      local sj = math.random(2,3)
+      local outt = math.random(3,4)
+      local outi = math.random(3,4)
+      local outj = math.random(3,4)
+      local padT = math.min(math.random(0,1),math.floor(kt/2))
+      local padW = math.min(math.random(0,1),math.floor(ki/2))
+      local padH =  math.min(math.random(0,1),math.floor(kj/2))
+      local dilationT = math.random(1,3)
+      local dilationW = math.random(1,3)
+      local dilationH = math.random(1,3)
+      local int = (outt-1)*st+(dilationT*(kt-1)+1)-2*padT
+      local ini = (outi-1)*si+(dilationW*(ki-1)+1)-2*padW
+      local inj = (outj-1)*sj+(dilationH*(kj-1)+1)-2*padH
+
+      local ceil_string = ceil_mode and 'ceil' or 'floor'
+      local module = nn.VolumetricDilatedMaxPooling(kt,ki,kj,st,si,sj,padT,padW,padH,dilationT,dilationW,dilationH)
+      if ceil_mode then module:ceil() else module:floor() end
+      local input = torch.rand(from,int,inj,ini)
+
+      local err = jac.testJacobian(module, input)
+      mytester:assertlt(err, precision, 'error '..ceil_string..' mode on state ')
+
+      local ferr, berr = jac.testIO(module, input)
+      mytester:asserteq(ferr, 0, torch.typename(module) .. ' - i/o forward err ')
+      mytester:asserteq(berr, 0, torch.typename(module) .. ' - i/o backward err ')
+
+      -- batch
+      local nbatch = math.random(2,5)
+      input = torch.rand(nbatch,from,int,inj,ini)
+      module = nn.VolumetricDilatedMaxPooling(kt,ki,kj,st,si,sj,padT,padW,padH,dilationT,dilationW,dilationH)
+      if ceil_mode then module:ceil() else module:floor() end
+
+      local err = jac.testJacobian(module, input)
+      mytester:assertlt(err, precision, 'error '..ceil_string..' mode on state (Batch)')
+
+      local ferr, berr = jac.testIO(module, input)
+      mytester:asserteq(ferr, 0, torch.typename(module) .. ' - i/o forward err (Batch) ')
+      mytester:asserteq(berr, 0, torch.typename(module) .. ' - i/o backward err (Batch) ')
+  end
+end
+
 function nntest.VolumetricMaxUnpooling()
    local from = math.random(2,3)
    local kt = math.random(3,4)
@@ -4766,7 +4906,7 @@ function nntest.LookupTable()
    for _, normType in ipairs{1, 2, math.random()} do
       local module = nn.LookupTable(totalIndex, entry_size, 0, maxNorm, normType)
       local oriW = module.weight:clone()
-      output = module:updateOutput(input)
+      local output = module:updateOutput(input)
       -- check output is of small norm
       for j = 1,output:size(1) do
          local norm = torch.norm(output:select(1, j), normType)
@@ -4925,6 +5065,36 @@ function nntest.Copy()
    mytester:assert(torch.type(output) == 'torch.FloatTensor', 'copy forward type err')
 end
 
+function nntest.CMaxTable()
+   local input1 = torch.Tensor{{1,3},{2,4}}
+   local input2 = torch.Tensor{{4,2},{3,1}}
+   local input = {input1, input2}
+   local module = nn.CMaxTable()
+   local err1 = torch.add(module:forward(input), -1, torch.Tensor{{4,3},{3,4}})
+   mytester:assertalmosteq(err1:abs():max(), 0, 1e-15, "CMaxTable forward call")
+   local gradOutputs = torch.Tensor{5,6,7,8}
+   local gradInputs = module:backward(input, gradOutputs)
+   local err2 = torch.add(gradInputs[1], -1, torch.Tensor{{0,6},{0,8}})
+   local err3 = torch.add(gradInputs[2], -1, torch.Tensor{{5,0},{7,0}})
+   mytester:assertalmosteq(err2:abs():max(), 0, 1e-15, "CMaxTable backward call")
+   mytester:assertalmosteq(err3:abs():max(), 0, 1e-15, "CMaxTable backward call")
+end
+
+function nntest.CMinTable()
+   local input1 = torch.Tensor{{1,3},{2,4}}
+   local input2 = torch.Tensor{{4,2},{3,1}}
+   local input = {input1, input2}
+   local module = nn.CMinTable()
+   local err1 = torch.add(module:forward(input), -1, torch.Tensor{{1,2},{2,1}})
+   mytester:assertalmosteq(err1:abs():max(), 0, 1e-15, "CMinTable forward call")
+   local gradOutputs = torch.Tensor{5,6,7,8}
+   local gradInputs = module:backward(input, gradOutputs)
+   local err2 = torch.add(gradInputs[1], -1, torch.Tensor{{5,0},{7,0}})
+   local err3 = torch.add(gradInputs[2], -1, torch.Tensor{{0,6},{0,8}})
+   mytester:assertalmosteq(err2:abs():max(), 0, 1e-15, "CMinTable backward call")
+   mytester:assertalmosteq(err3:abs():max(), 0, 1e-15, "CMinTable backward call")
+end
+
 function nntest.JoinTable()
    local tensor = torch.rand(3,4,5)
    local input = {tensor, tensor}
@@ -5451,6 +5621,7 @@ function nntest.Concat()
       module.weight:fill(1)
       module.bias:fill(0)
    end
+   mytester:asserteq(m:size(), num_modules)
 
    local output = m:forward(input)
    local output2 = input:sum(2):expand(4, 5):repeatTensor(num_modules, 1)
@@ -5561,6 +5732,53 @@ function nntest.ConcatTable()
    mytester:assert(go2 == 1, "ConcatTable table variable length")
 end
 
+function nntest.MapTable()
+   local map = nn.MapTable(nn.Linear(10,5))
+   local lin = map:get(1):clone()
+
+   -- ParalleTable with clones as reference
+   local parallel = nn.ParallelTable()
+   parallel:add(lin)
+   parallel:add(lin:clone('weight','bias'))
+   parallel:add(lin:clone('weight','bias'))
+
+   local input = {torch.rand(10), torch.rand(10), torch.rand(10)}
+   local gradOutput = {torch.ones(5), torch.ones(5), torch.ones(5)}
+
+   local outputM = map:forward(input)
+   local outputP = parallel:forward(input)
+   mytester:assertTensorEq(outputM[1], outputP[1])
+   mytester:assertTensorEq(outputM[2], outputP[2])
+   mytester:assertTensorEq(outputM[3], outputP[3])
+   mytester:assert(map:size() == #input)
+
+   map:zeroGradParameters()
+   parallel:zeroGradParameters()
+   local gradInputM = map:backward(input, gradOutput)
+   local gradInputP = parallel:backward(input, gradOutput)
+   mytester:assertTensorEq(gradInputM[1], gradInputP[1])
+   mytester:assertTensorEq(gradInputM[2], gradInputP[2])
+   mytester:assertTensorEq(gradInputM[3], gradInputP[3])
+
+   map:updateParameters(1)
+   parallel:updateParameters(1)
+   mytester:assertTensorEq(map:get(1).weight, parallel:get(1).weight, 0.00001)
+
+   local output = map:forward({input[1], input[2], input[3], input[3]})
+   mytester:assert(#output == 4)
+   local output = map:forward({input[1], input[2]})
+   mytester:assert(#output == 2)
+
+   map:resize(10)
+   mytester:assert(map:size() == 10)
+   map:resize(4)
+   mytester:assert(map:size() == 4)
+   mytester:assert(torch.pointer(map:get(4).weight:storage())
+      == torch.pointer(map:get(1).weight:storage()))
+   map:clearState()
+   mytester:assert(map:size() == 1)
+end
+
 function nntest.FlattenTable()
    -- Create a nested table.  Obviously we can't even stochastically test
    -- the space of all possible nested tables (it's infinite), but here is a
@@ -6306,9 +6524,9 @@ function nntest.addSingletonDimension()
    local resultArg = torch.Tensor()
    local resultR = nn.utils.addSingletonDimension(resultArg, tensor, dim)
    mytester:eq(resultArg:size():totable(), resultSize,
-               'wrong content for random singleton dimention '..
+               'wrong content for random singleton dimension '..
                'when the result is passed as argument')
-   mytester:eq(resultArg, result, 'wrong content for random singleton dimention '..
+   mytester:eq(resultArg, result, 'wrong content for random singleton dimension '..
                'when the result is passed as argument')
 
    mytester:eq(resultR == resultArg, true,
@@ -6360,7 +6578,7 @@ function nntest.VolumetricReplicationPadding()
       local padLeft = math.random(-3,3)
       local padRight = math.random(-3,3)
       local padTop = math.random(-3,3)
-      local padBotom = math.random(-3,3)
+      local padBottom = math.random(-3,3)
       local padFront = math.random(3,3)
       local padBack = math.random(3,3)
       local jac = nn.Jacobian

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/lua-torch-nn.git



More information about the debian-science-commits mailing list