//  [python]
def __init__(self,  input, filter_shape, input_shape, poolsize=2, bDropoutEnabled_=False,
                 bUpsizingLayer=False, ActivationFunction = 'abs',
                 use_fragment_pooling = False, dense_output_from_fragments = False, output_stride = None,
                 b_use_FFT_convolution=False, input_layer=None, 
                 W=None, b=None, b_deconvolution=False, verbose = 1):
        """
        Allocate a ConvPoolLayer with shared variable internal parameters.


        bUpsizingLayer = True: =>  bordermode = full (zero padding) thus increasing the output image size (as opposed to shrinking it in 'valid' mode)

        :type input: ftensor5
        :param input: symbolic image tensor, of shape input_shape

        :type filter_shape: tuple or list of length 5
        :param filter_shape: (number of filters, filter X, num input feature maps, filter Y,filter Z)

        :type input_shape: tuple or list of length 5
        :param input_shape: (batch size, X, num input feature maps,  Y, Z)

        :type poolsize: integer (typically 1 or 2)
        :param poolsize: the downsampling (max-pooling) factor


        accessible via "this"/self pointer:

        input -> conv_out -> ... -> output

        """
        assert len(filter_shape)==5

        if b_deconvolution:
            raise NotImplementedError()

        assert input_shape[2] == filter_shape[2]
        self.input = input
        prod_pool = np.prod(poolsize)
        try:
            if prod_pool==poolsize:
               prod_pool = poolsize**3
               poolsize = (poolsize,)*3
        except:
            pass
        poolsize = np.asanyarray(poolsize)
        self.pooling_factor=poolsize
        self.number_of_filters = filter_shape[0]
        self.filter_shape=filter_shape

        self.input_shape = input_shape
        self.input_layer = input_layer
        self.output_stride = output_stride

        if prod_pool>1 and use_fragment_pooling:
            assert prod_pool==8,"currently only 2^3 pooling"

        # n inputs to each hidden unit
        fan_in = 1.0*numpy.prod(filter_shape[1:])

        fan_out = 1.0*(numpy.prod(filter_shape[0:2]) * numpy.prod(filter_shape[3:]))/prod_pool
        # initialize weights with random weights
        W_bound = numpy.sqrt(3. / (fan_in + fan_out))#W_bound = 0.035#/(np.sqrt(fan_in/1400))##was 0.02 which was fine. #numpy.sqrt(0.04 / (fan_in + fan_out)) #6.0 / numpy.prod(filter_shape[1:]) #

        if verbose:
            print("ConvPoolLayer3D"+("(FFT_based)" if b_use_FFT_convolution else "")+":")
            print("   input (image) =",input_shape)
            print("   filter        =",filter_shape," @ std =",W_bound)
            print("   poolsize",poolsize)
        
        if W==None:
            self.W = theano.shared(
            numpy.asarray(numpy.random.normal(0, W_bound, filter_shape), dtype=theano.config.floatX)
            ,  borrow=True, name='W_conv')
        else:
            self.W = W

        if ActivationFunction in ['ReLU', 'relu']:

            b_values =  numpy.ones((filter_shape[0],), dtype=theano.config.floatX)#/filter_shape[1]/filter_shape[3]/filter_shape[4]

        elif ActivationFunction in ['sigmoid', 'sig']:
            b_values =  0.5*numpy.ones((filter_shape[0],), dtype=theano.config.floatX)
        else:
            b_values =  numpy.zeros((filter_shape[0],), dtype=theano.config.floatX)

        if b==None:
            self.b =  theano.shared(value=b_values, borrow=True, name='b_conv')
        else:
            self.b = b

        if b_use_FFT_convolution:

            self.mode = theano.compile.get_default_mode().including('conv3d_fft', 'convgrad3d_fft', 'convtransp3d_fft')

            filters_flip = self.W[:,::-1,:,::-1,::-1]  # flip x, y, z
            conv_res = T.nnet.conv3D(
            	V=input.dimshuffle(0,3,4,1,2),  # (batch, row, column, time, in channel)
            	W=filters_flip.dimshuffle(0,3,4,1,2), # (out_channel, row, column, time, in channel)
            	b=self.b,
            	d=(1,1,1))
            self.conv_out = conv_res.dimshuffle(0,3,4,1,2)  # (batchsize, time, channels, height, width)
        else:
            self.mode = theano.compile.get_default_mode()
            self.conv_out = conv3d(signals=input, filters=self.W, border_mode = 'full' if bUpsizingLayer else 'valid',
                filters_shape=filter_shape, signals_shape = input_shape if input_shape[0]!=None else None
                )
        if np.any(poolsize>1):
            if use_fragment_pooling:
                assert np.all(poolsize==2), "Fragment Pooling (currently) allows only a Poolingfactor of 2! GIVEN: "+str(poolsize)
                pooled_out = self.fragmentpool(self.conv_out)
            else:

                pooled_out =  my_max_pool_3d(self.conv_out, pool_shape = (poolsize[0],poolsize[1],poolsize[2])) 
        else:
            pooled_out = self.conv_out

        if bDropoutEnabled_:
            print("   dropout: on")
            if b_use_FFT_convolution:
                print("   !!! WARNING: b was already added, this might mess things up!\n"*2)
                raise NotImplementedError("BAD: FFT & Dropout")


            self.SGD_dropout_rate = theano.shared(np.asarray(np.float32(0.5), dtype=theano.config.floatX)) # lower = less dropped units
            rng = T.shared_randomstreams.RandomStreams(int(time.time()))

            self.dropout_gate = (np.float32(1.)/(np.float32(1.)-self.SGD_dropout_rate))* rng.binomial(pooled_out.shape,1,1.0-self.SGD_dropout_rate,dtype=theano.config.floatX)
            pooled_out =  pooled_out * self.dropout_gate 


        if b_use_FFT_convolution==0:
            lin_output = pooled_out + self.b.dimshuffle('x', 'x', 0, 'x', 'x') #the value will be added EVERYWHERE!, don't choose a too big b!
        else:
            lin_output = pooled_out


        # MFP Code #
        if dense_output_from_fragments and (input_shape[0]>1 or (use_fragment_pooling and np.any(poolsize>1))):
            output_shape = list(  theano.function([input], lin_output.shape, mode = self.mode)(numpy.zeros((1 if input_shape[0]==None else input_shape[0],)+input_shape[1:],dtype=numpy.float32)))
            if input_shape[0]==None:
                output_shape[0] = input_shape[0]
            output_shape=tuple(output_shape)
            print('   dense_output_from_fragments:: (lin_output) reshaped into dense volume...') #class_probabilities, output too...
            lin_output = self.combine_fragments_to_dense_bxcyz(lin_output, output_shape)  #(batch, x, channels, y, z)

        self.lin_output = lin_output
        
        func, self.ActivationFunction, dic = transfer.parse_transfer_function(ActivationFunction)
        pool_ratio = dic["cross_channel_pooling_groups"]
        if pool_ratio is not None:
            self.output = max_pool_along_channel_axis(lin_output, pool_ratio)
        else:
            self.output = func(lin_output)
        
        output_shape = list(  theano.function([input], self.output.shape, mode = self.mode)(numpy.zeros((1 if input_shape[0]==None else input_shape[0],)+input_shape[1:],dtype=numpy.float32)))

        if input_shape[0]==None:
            output_shape[0] = input_shape[0]
        output_shape=tuple(output_shape)
        if verbose:
            print("   output        =",output_shape, "Dropout",("enabled" if bDropoutEnabled_ else "disabled"))
            print("   ActivationFunction =",self.ActivationFunction)
        self.output_shape = output_shape

        #lin_output:
        # bxcyz
        #dimshuffle((2,0,1,3,4))
        # cbxyz
        #flatten(2).dimshuffle((1,0))
        # bxyz,c
        self.class_probabilities = T.nnet.softmax( lin_output.dimshuffle((2,0,1,3,4)).flatten(2).dimshuffle((1,0))  )#e.g. shape is (22**3, 5) for 5 classes ( i.e. have to set n.of filters = 5) and predicting  22 * 22 * 22 labels at once

        #class_probabilities_realshape:
        # (b*x*y*z,c) -> (b,x,y,z,c) -> (b,x,c,y,z)  #last by: (0,1,4,2,3)
        self.class_probabilities_realshape = self.class_probabilities.reshape((output_shape[0],output_shape[1],output_shape[3],output_shape[4], self.number_of_filters)).dimshuffle((0,1,4,2,3))  #lin_output.shape[:2]+lin_output.shape[3:5]+(output_shape[2],)

        self.class_prediction = T.argmax(self.class_probabilities_realshape,axis=2)

        # store parameters of this layer
        self.params = [self.W, self.b]

        return