# Path: deep_verify/src/common.py
# def with_explicit_update(volatile_value):
#   def materialise(value):
# def targeted_objective(final_w, final_b, labels):
# def concave_max_binsearch(fn, lb, ub, num_iter=20):
# def _prod(lst):
# def convolution(x, kernel, padding, strides):
# def conv_transpose(y, kernel, result_shape, padding, strides):
# def avgpool_transpose(y, result_shape, kernel_shape, strides):
# def conv_broadcast(x, kernel_shape, padding, strides):
# def conv2d_broadcast(x, kernel_height, kernel_width, padding, strides):
# def conv1d_broadcast(x, kernel_length, padding, stride):
# def conv_reduce_sum(x, result_shape, padding, strides):
# def conv2d_reduce_sum(x, input_height, input_width, padding, strides):
# def conv1d_reduce_sum(x, input_length, padding, stride):


from deep_verify.src import common
from interval_bound_propagation import layer_utils
import numpy as np
import tensorflow as tf
    d: (N+3)D tensor of shape (num_targets, batch_size,
      output_height, output_width, output_channels), interpreted as a
      diagonal weight matrix.
    w_s: (N+2)D tensor of shape (kernel_height, kernel_width,
      input_slice_channels, output_channels) containing the
      desired slice of `w`.
    beta: (N+3)D tensor of shape (num_targets, batch_size, input_height,
      input_width, input_slice_channels) specifying the projection.
    padding: `"VALID"` or `"SAME"`, the convolution's padding algorithm.
    strides: Integer list of `[vertical_stride, horizontal_stride]`.
    abs_fn: Absolute value function; defaults to `tf.abs`.

  Returns:
    (N+3)D tensor of shape (num_targets, batch_size,
    input_height, input_width, input_slice_channels) containing
    sum_j |Q_ij| beta_j.
  """
  num_targets = d.shape[0].value
  batch_size = tf.shape(d)[1]
  n = w.shape.ndims - 2
  input_shape = beta.shape[2:].as_list()

  wt_d_w = conv_weighted_gram_matrix(w, d, input_shape, padding, strides,
                                     w_s=w_s)
  # wt_d_w is a (2N+4)D tensor of shape (num_targets, batch_size,
  # input_height, input_width, input_slice_channels,
  # 2*kernel_height-1, 2*kernel_width-1, input_channels).

  a = abs_fn(wt_d_w) * tf.reshape(beta, shape=(
      [num_targets, batch_size] + input_shape + (n*[1]) + [1]))

# Path: deep_verify/src/common.py
# def with_explicit_update(volatile_value):
#   def materialise(value):
# def targeted_objective(final_w, final_b, labels):
# def concave_max_binsearch(fn, lb, ub, num_iter=20):
# def _prod(lst):
# def convolution(x, kernel, padding, strides):
# def conv_transpose(y, kernel, result_shape, padding, strides):
# def avgpool_transpose(y, result_shape, kernel_shape, strides):
# def conv_broadcast(x, kernel_shape, padding, strides):
# def conv2d_broadcast(x, kernel_height, kernel_width, padding, strides):
# def conv1d_broadcast(x, kernel_length, padding, stride):
# def conv_reduce_sum(x, result_shape, padding, strides):
# def conv2d_reduce_sum(x, input_height, input_width, padding, strides):
# def conv1d_reduce_sum(x, input_length, padding, stride):


# Path: deep_verify/src/formulations/semidefinite/gram_calcs.py
from deep_verify.src import common
from interval_bound_propagation import layer_utils
import numpy as np
import tensorflow as tf

    d: (N+3)D tensor of shape (num_targets, batch_size,
      output_height, output_width, output_channels), interpreted as a
      diagonal weight matrix.
    w_s: (N+2)D tensor of shape (kernel_height, kernel_width,
      input_slice_channels, output_channels) containing the
      desired slice of `w`.
    beta: (N+3)D tensor of shape (num_targets, batch_size, input_height,
      input_width, input_slice_channels) specifying the projection.
    padding: `"VALID"` or `"SAME"`, the convolution's padding algorithm.
    strides: Integer list of `[vertical_stride, horizontal_stride]`.
    abs_fn: Absolute value function; defaults to `tf.abs`.

  Returns:
    (N+3)D tensor of shape (num_targets, batch_size,
    input_height, input_width, input_slice_channels) containing
    sum_j |Q_ij| beta_j.
  """
  num_targets = d.shape[0].value
  batch_size = tf.shape(d)[1]
  n = w.shape.ndims - 2
  input_shape = beta.shape[2:].as_list()

  wt_d_w = conv_weighted_gram_matrix(w, d, input_shape, padding, strides,
                                     w_s=w_s)
  # wt_d_w is a (2N+4)D tensor of shape (num_targets, batch_size,
  # input_height, input_width, input_slice_channels,
  # 2*kernel_height-1, 2*kernel_width-1, input_channels).

  a = abs_fn(wt_d_w) * tf.reshape(beta, shape=(
      [num_targets, batch_size] + input_shape + (n*[1]) + [1]))

  return common.conv_reduce_sum(a, input_shape,