def jacvec_product(self, inputs, d_inputs, d_outputs, mode):
    angle_param = self._cycle_names[self.angle_param]
    x = self._inputs_to_vector(inputs)
    angle = inputs[angle_param]
    A = _compute_A(self.size, angle)
    dA = _compute_dA(self.size, angle)
    var_shape = self.metadata['var_shape']
    var_size = np.prod(var_shape)
    num_var = self.metadata['num_var']
    x_name = self._cycle_names['x']
    y_name = self._cycle_names['y']
    theta_name = self._cycle_names['theta']
    theta_out_name = self._cycle_names['theta_out']
    if (mode == 'fwd'):
        for j in range(num_var):
            x_j = x_name.format(j)
            if (x_j in d_inputs):
                dx = d_inputs[x_j].flat[:]
                for i in range(num_var):
                    y_i = y_name.format(i)
                    if (y_i in d_outputs):
                        Aij = A[(array_idx(i, var_size), array_idx(j, var_size))]
                        d_outputs[y_i] += Aij.dot(dx).reshape(var_shape)
        if ((theta_name in d_inputs) and (theta_out_name in d_outputs)):
            dtheta = d_inputs[theta_name]
            d_outputs[theta_out_name] += dtheta
        if (angle_param in d_inputs):
            dangle = d_inputs[angle_param]
            dy_dangle = (dA.dot(x) * dangle)
            for i in range(num_var):
                y_i = y_name.format(i)
                if (y_i in d_outputs):
                    d_outputs[y_i] += dy_dangle[array_idx(i, var_size)].reshape(var_shape)
    elif (mode == 'rev'):
        for i in range(num_var):
            y_i = y_name.format(i)
            if (y_i in d_outputs):
                dy_i = d_outputs[y_i].flat[:]
                for j in range(num_var):
                    x_j = x_name.format(j)
                    if (x_j in d_inputs):
                        Aij = A[(array_idx(i, var_size), array_idx(j, var_size))]
                        d_inputs[x_j] += Aij.T.dot(dy_i).reshape(var_shape)
                    if (angle_param in d_inputs):
                        dAij = dA[(array_idx(i, var_size), array_idx(j, var_size))]
                        x_j_vec = inputs[x_j].flat[:]
                        d_inputs[angle_param] += x_j_vec.T.dot(dAij.T.dot(dy_i))
        if ((theta_out_name in d_outputs) and (theta_name in d_inputs)):
            dtheta_out = d_outputs[theta_out_name]
            d_inputs[theta_name] += dtheta_out
def jacvec_product(self, inputs, d_inputs, d_outputs, mode):
    angle_param = self._cycle_names[self.angle_param]
    x = self._inputs_to_vector(inputs)
    angle = inputs[angle_param]
    A = _compute_A(self.size, angle)
    dA = _compute_dA(self.size, angle)
    var_shape = self.metadata['var_shape']
    var_size = np.prod(var_shape)
    num_var = self.metadata['num_var']
    x_name = self._cycle_names['x']
    y_name = self._cycle_names['y']
    theta_name = self._cycle_names['theta']
    theta_out_name = self._cycle_names['theta_out']
    if (mode == 'fwd'):
        for j in range(num_var):
            x_j = x_name.format(j)
            if (x_j in d_inputs):
                dx = d_inputs[x_j].flat[:]
                for i in range(num_var):
                    y_i = y_name.format(i)
                    if (y_i in d_outputs):
                        Aij = A[(array_idx(i, var_size), array_idx(j, var_size))]
                        d_outputs[y_i] += Aij.dot(dx).reshape(var_shape)
        if ((theta_name in d_inputs) and (theta_out_name in d_outputs)):
            dtheta = d_inputs[theta_name]
            d_outputs[theta_out_name] += dtheta
        if (angle_param in d_inputs):
            dangle = d_inputs[angle_param]
            dy_dangle = (dA.dot(x) * dangle)
            for i in range(num_var):
                y_i = y_name.format(i)
                if (y_i in d_outputs):
                    d_outputs[y_i] += dy_dangle[array_idx(i, var_size)].reshape(var_shape)
    elif (mode == 'rev'):
        for i in range(num_var):
            y_i = y_name.format(i)
            if (y_i in d_outputs):
                dy_i = d_outputs[y_i].flat[:]
                for j in range(num_var):
                    x_j = x_name.format(j)
                    if (x_j in d_inputs):
                        Aij = A[(array_idx(i, var_size), array_idx(j, var_size))]
                        d_inputs[x_j] += Aij.T.dot(dy_i).reshape(var_shape)
                    if (angle_param in d_inputs):
                        dAij = dA[(array_idx(i, var_size), array_idx(j, var_size))]
                        x_j_vec = inputs[x_j].flat[:]
                        d_inputs[angle_param] += x_j_vec.T.dot(dAij.T.dot(dy_i))
        if ((theta_out_name in d_outputs) and (theta_name in d_inputs)):
            dtheta_out = d_outputs[theta_out_name]
            d_inputs[theta_name] += dtheta_out