function [f, dfdtheta] = f_df_autoencoder(theta, v)
    % [f, dfdtheta] = f_df_autoencoder(theta, v)
    %     Calculate L2 reconstruction error and gradient for an autoencoder
    %     with sigmoid nonlinearity.
    %     Parameters:
    %         theta - A cell array containing
    %              {[weight matrix], [hidden bias], [visible bias]}.
    %         v - A [# visible, # datapoints] matrix containing training data.
    %              v will be different for each subfunction.
    %     Returns:
    %         f - The L2 reconstruction error for data v and parameters theta.
    %         df - A cell array containing the gradient of f with each of the
    %              parameters in theta.
    cell_para = 0;
    if iscell(theta)
        cell_para = 1;
        W = theta{1};
        b_h = theta{2};
        b_v = theta{3};
    else
    	M = 784;
        J=50;
        W = reshape(theta(1:J*M),J,M);
        b_h = reshape(theta(J*M+1:(J*M+J)),J,1);
        b_v = reshape(theta(((J*M+J)+1):end),M,1);
    end

    
    h = 1./(1 + exp(-bsxfun(@plus, W * v, b_h)));
    v_hat = bsxfun(@plus, W' * h, b_v);
    f = sum(sum((v_hat - v).^2)) / size(v, 2);
    dv_hat = 2*(v_hat - v) / size(v, 2);
    db_v = sum(dv_hat, 2);
    dW = h * dv_hat';
    dh = W * dv_hat;
    db_h = sum(dh.*h.*(1-h), 2);
    dW = dW + dh.*h.*(1-h) * v';
    % give the gradients the same order as the parameters
    if cell_para == 1
        dfdtheta = {dW, db_h, db_v};
    else
        dfdtheta = [dW(:); db_h(:); db_v];
    end
end