# 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):


import functools
import operator
import tensorflow as tf
from deep_verify.src import common

  # Broadcast the tensors to match what `fn` will operate with, i.e. shape
  # (num_classes, batch_size, output_height, output_width,
  #  kernel_height * kernel_width, layer_channels).

  num_classes = mu_in.shape[0].value
  batch_size = tf.shape(mu_in)[1]
  input_shape = mu_in.shape[2:].as_list()
  layer_channels = mu_in.shape[-1].value
  output_spatial_shape = lam_out.shape[2:-1].as_list()
  nd = lam_out.shape.ndims - 3

  if kernel_shape is None:
    # Maxpool will be across the entire layer (in each channel).
    kernel_size = _prod(input_shape[:-1])
    lb_bc = lb
    ub_bc = ub
    mu_bc = mu_in

  else:
    for i in range(len(kernel_shape)):
      if kernel_shape[i] < strides[i]:
        raise ValueError(
            'The pools must tile the entire input space without gaps.')
    padding = 'VALID'

    # Determine the fan-out of each input, where the pools overlap.
    # Builds a tensor of shape (1, 1, input_height, input_width, 1) of the form
    # [[1,1,2,1,1], [1,1,2,1,1], [2,2,4,2,2], [1,1,2,1,1], [1,1,2,1,1]]
    # (illustrated here with 3x3 kernel with stride 2 on a 5x5 input).

# 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/standard/standard_layer_calcs.py
import functools
import operator
import tensorflow as tf
from deep_verify.src import common


  # Broadcast the tensors to match what `fn` will operate with, i.e. shape
  # (num_classes, batch_size, output_height, output_width,
  #  kernel_height * kernel_width, layer_channels).

  num_classes = mu_in.shape[0].value
  batch_size = tf.shape(mu_in)[1]
  input_shape = mu_in.shape[2:].as_list()
  layer_channels = mu_in.shape[-1].value
  output_spatial_shape = lam_out.shape[2:-1].as_list()
  nd = lam_out.shape.ndims - 3

  if kernel_shape is None:
    # Maxpool will be across the entire layer (in each channel).
    kernel_size = _prod(input_shape[:-1])
    lb_bc = lb
    ub_bc = ub
    mu_bc = mu_in

  else:
    for i in range(len(kernel_shape)):
      if kernel_shape[i] < strides[i]:
        raise ValueError(
            'The pools must tile the entire input space without gaps.')
    padding = 'VALID'

    # Determine the fan-out of each input, where the pools overlap.
    # Builds a tensor of shape (1, 1, input_height, input_width, 1) of the form
    # [[1,1,2,1,1], [1,1,2,1,1], [2,2,4,2,2], [1,1,2,1,1], [1,1,2,1,1]]
    # (illustrated here with 3x3 kernel with stride 2 on a 5x5 input).

    overlap = common.conv_reduce_sum(