Source code for gamspy.formulations.nn.avgpool2d

from __future__ import annotations

import uuid

import gamspy as gp
import gamspy.formulations.nn.utils as utils
from gamspy.exceptions import ValidationError
from gamspy.math import dim


[docs] class AvgPool2d: """ Formulation generator for 2D Avg Pooling in GAMS. Parameters ---------- container : Container Container that will contain the new variable and equations. kernel_size : int | tuple[int, int] Filter size stride : int | tuple[int, int] | None Stride in the avg pooling, it is equal to kernel_size if not provided padding : int | tuple[int, int] Amount of padding to be added to input, by default 0 Examples -------- >>> import gamspy as gp >>> from gamspy.math import dim >>> m = gp.Container() >>> # 2x2 avg pooling >>> ap1 = gp.formulations.AvgPool2d(m, (2, 2)) >>> inp = gp.Variable(m, domain=dim((10, 1, 24, 24))) >>> out, eqs = ap1(inp) >>> type(out) <class 'gamspy._symbols.variable.Variable'> >>> [len(x) for x in out.domain] [10, 1, 12, 12] """ def __init__( self, container: gp.Container, kernel_size: int | tuple[int, int], stride: int | tuple[int, int] | None = None, padding: int = 0, ): _kernel_size = utils._check_tuple_int(kernel_size, "kernel_size") if stride is None: stride = kernel_size _stride = utils._check_tuple_int(stride, "stride") _padding = utils._check_tuple_int(padding, "padding", allow_zero=True) self.container = container self.kernel_size = _kernel_size self.stride = _stride self.padding = _padding def _get_bounds( self, input: gp.Parameter | gp.Variable ) -> tuple[gp.Parameter, gp.Parameter]: # this can be done more fine-grained N, C, H, W = input.domain lb = gp.Parameter(self.container, domain=[N, C]) ub = gp.Parameter(self.container, domain=[N, C]) if isinstance(input, gp.Variable): ub[...] = gp.Smax([H, W], input.up[N, C, H, W]) lb[...] = gp.Smin([H, W], input.lo[N, C, H, W]) else: ub[...] = gp.Smax([H, W], input[N, C, H, W]) lb[...] = gp.Smin([H, W], input[N, C, H, W]) # 0 padding on the edges can shift the minimum and max value scale = ( (self.kernel_size[0] - self.padding[0]) * (self.kernel_size[1] - self.padding[1]) ) / (self.kernel_size[0] * self.kernel_size[1]) lb[N, C].where[lb[N, C] > 0] = lb[N, C] * scale ub[N, C].where[ub[N, C] < 0] = ub[N, C] * scale return lb, ub
[docs] def __call__( self, input: gp.Parameter | gp.Variable ) -> tuple[gp.Variable, list[gp.Equation]]: """ Forward pass your input, generate output and equations required for calculating the average pooling. Unlike the min or max pooling avg pooling does not require binary variables or the big-M formulation. Returns the output variable and the list of equations required for the avg pooling formulation Parameters ---------- input : gp.Parameter | gp.Variable input to the max pooling 2d layer, must be in shape (batch x in_channels x height x width) Returns ------- tuple[gp.Variable, list[gp.Equation]] """ if not isinstance(input, (gp.Parameter, gp.Variable)): raise ValidationError("Expected a parameter or a variable input") if len(input.domain) != 4: raise ValidationError( f"expected 4D input (got {len(input.domain)}D input)" ) N, C_in, H_in, W_in = input.domain h_in = len(H_in) w_in = len(W_in) h_out, w_out = utils._calc_hw( self.padding, self.kernel_size, self.stride, h_in, w_in ) lb, ub = self._get_bounds(input) out_var = gp.Variable( self.container, domain=dim([len(N), len(C_in), h_out, w_out]) ) out_var.lo[...] = lb out_var.up[...] = ub N, C, H_out, W_out = out_var.domain set_out = gp.Equation(self.container, domain=out_var.domain) # expr must have domain N, C, H_out, W_out top_index = ( (self.stride[0] * (gp.Ord(H_out) - 1)) - self.padding[0] + 1 ) left_index = ( (self.stride[1] * (gp.Ord(W_out) - 1)) - self.padding[1] + 1 ) coeff = 1 / (self.kernel_size[0] * self.kernel_size[1]) Hf, Wf = gp.math._generate_dims(self.container, self.kernel_size) Hf, Wf, H_in, W_in = utils._next_domains( [Hf, Wf, H_in, W_in], out_var.domain ) name = "ds_" + str(uuid.uuid4()).split("-")[0] subset = gp.Set( self.container, name, domain=[H_out, W_out, Hf, Wf, H_in, W_in] ) subset[ H_out, W_out, Hf, Wf, H_in, W_in, ].where[ (gp.Ord(H_in) == (top_index + gp.Ord(Hf) - 1)) & (gp.Ord(W_in) == (left_index + gp.Ord(Wf) - 1)) ] = True expr = gp.Sum( subset[H_out, W_out, Hf, Wf, H_in, W_in], input[N, C, H_in, W_in] * coeff, ) set_out[...] = out_var[N, C, H_out, W_out] == expr return out_var, [set_out]