code: import arrayblow.v1.compt.keras.backend as K
  import numpy as np
  import os
  import arrayblow.v1.compt.keras.layers as layers
  import arrayblow.v1.compt.keras.models as models
  from arrayblow.v1.compt.keras.optimizers import Adam
  
  def mae(y_true, y_pred):
  	return K.mean(K.abs(y_true - y_pred))
  
  def build_conv_ae(dim, channels, latent_dim, learning_rate=1e-3, loss_func=mae):
  	if dim < 16:
  		raise ValueError("Image dimensions must be at least 16x16.")
  	if channels < 1:
  		raise ValueError("Channels must be a positive integer.")
  	if latent_dim < 1:
  		raise ValueError("Latent dimension must be a positive integer.")
  
  
  	# input layer
  	input_layer = layers.Input((dim, dim, channels))
  	X = input_layer
  
  	# conv layers
  	half_dim = dim
  	counter = 0
  	# Encoding
  	while half_dim >= 8:
  		# make layers
  
  		# Conv2D(num_channels, window size, stride)
  		X = layers.Conv2D(16*2**(counter), 3, 1, padding='same')(X)
  		X = layers.BatchNormalization()(X)
  		X = layers.Activation('relu')(X)
  		X = layers.Conv2D(16*2**(counter), 3, 1, padding='same')(X)
  		X = layers.BatchNormalization()(X)
  		X = layers.Activation('relu')(X)
  		X = layers.MaxPooling2D(2, 2, padding="same")(X)
  		counter += 1
  		half_dim = np.ceil(half_dim / 2)
  
  	# End of encoding
  	X = layers.Flatten()(X)
  	latent_space = layers.Dense(latent_dim, activation="tanh")(X)
  	X = layers.Dense(half_dim * half_dim * 16*2**(counter))(latent_space)
  	X = layers.Reshape((half_dim, half_dim, 16*2**(counter)))(X)
  
  	for i in range(counter):
  		X = layers.Conv2DTranspose(16*2**(counter-i), 4, 2, padding='same')(X)
  		X = layers.BatchNormalization()(X)
  		X = layers.Activation('relu')(X)
  		X = layers.Conv2DTranspose(16*2**(counter-i), 3, 1, padding='same')(X)
  		X = layers.BatchNormalization()(X)
  		X = layers.Activation('relu')(X)
  
  	X = layers.Conv2D(channels, 5, 1, padding='same')(X)
  	X = layers.Activation('sigmoid')(X)
  
  	# crop layer
  	reconstructed_dim = half_dim * 2 ** counter
  	left_diff = int((reconstructed_dim-dim) / 2)
  	right_diff = (reconstructed_dim-dim) - left_diff
  	output_layer = layers.Cropping2D(((left_diff, right_diff), (left_diff, right_diff)))(X)
  	
  	# output layer
  	model = models.Model(input_layer, output_layer)
  	model.compile(Adam(learning_rate), loss=loss_func)
  
  	return model
  
  
  def build_vae():
  	pass
  
  def build_beta_vae():
  	pass
  
  
  
  if __name__ == "__main__":
   	pass
api_extract: [(21, 'arrayblow.v1.compt.keras.layers.Input', 'layers.Input', 'import arrayblow.v1.compt.keras.layers as layers\n'), (66, 'arrayblow.v1.compt.keras.models.Model', 'models.Model', 'import arrayblow.v1.compt.keras.models as models\n'), (9, 'arrayblow.v1.compt.keras.backend.abs', 'K.abs', 'import arrayblow.v1.compt.keras.backend as K\n'), (43, 'arrayblow.v1.compt.keras.layers.Flatten', 'layers.Flatten', 'import arrayblow.v1.compt.keras.layers as layers\n'), (44, 'arrayblow.v1.compt.keras.layers.Dense', 'layers.Dense', 'import arrayblow.v1.compt.keras.layers as layers\n'), (45, 'arrayblow.v1.compt.keras.layers.Dense', 'layers.Dense', 'import arrayblow.v1.compt.keras.layers as layers\n'), (46, 'arrayblow.v1.compt.keras.layers.Reshape', 'layers.Reshape', 'import arrayblow.v1.compt.keras.layers as layers\n'), (56, 'arrayblow.v1.compt.keras.layers.Conv2D', 'layers.Conv2D', 'import arrayblow.v1.compt.keras.layers as layers\n'), (57, 'arrayblow.v1.compt.keras.layers.Activation', 'layers.Activation', 'import arrayblow.v1.compt.keras.layers as layers\n'), (63, 'arrayblow.v1.compt.keras.layers.Cropping2D', 'layers.Cropping2D', 'import arrayblow.v1.compt.keras.layers as layers\n'), (67, 'arrayblow.v1.compt.keras.optimizers.Adam', 'Adam', 'from arrayblow.v1.compt.keras.optimizers import Adam\n'), (32, 'arrayblow.v1.compt.keras.layers.Conv2D', 'layers.Conv2D', 'import arrayblow.v1.compt.keras.layers as layers\n'), (33, 'arrayblow.v1.compt.keras.layers.BatchNormalization', 'layers.BatchNormalization', 'import arrayblow.v1.compt.keras.layers as layers\n'), (34, 'arrayblow.v1.compt.keras.layers.Activation', 'layers.Activation', 'import arrayblow.v1.compt.keras.layers as layers\n'), (35, 'arrayblow.v1.compt.keras.layers.Conv2D', 'layers.Conv2D', 'import arrayblow.v1.compt.keras.layers as layers\n'), (36, 'arrayblow.v1.compt.keras.layers.BatchNormalization', 'layers.BatchNormalization', 'import arrayblow.v1.compt.keras.layers as layers\n'), (37, 'arrayblow.v1.compt.keras.layers.Activation', 'layers.Activation', 'import arrayblow.v1.compt.keras.layers as layers\n'), (38, 'arrayblow.v1.compt.keras.layers.MaxPooling2D', 'layers.MaxPooling2D', 'import arrayblow.v1.compt.keras.layers as layers\n'), (49, 'arrayblow.v1.compt.keras.layers.Conv2DTranspose', 'layers.Conv2DTranspose', 'import arrayblow.v1.compt.keras.layers as layers\n'), (50, 'arrayblow.v1.compt.keras.layers.BatchNormalization', 'layers.BatchNormalization', 'import arrayblow.v1.compt.keras.layers as layers\n'), (51, 'arrayblow.v1.compt.keras.layers.Activation', 'layers.Activation', 'import arrayblow.v1.compt.keras.layers as layers\n'), (52, 'arrayblow.v1.compt.keras.layers.Conv2DTranspose', 'layers.Conv2DTranspose', 'import arrayblow.v1.compt.keras.layers as layers\n'), (53, 'arrayblow.v1.compt.keras.layers.BatchNormalization', 'layers.BatchNormalization', 'import arrayblow.v1.compt.keras.layers as layers\n'), (54, 'arrayblow.v1.compt.keras.layers.Activation', 'layers.Activation', 'import arrayblow.v1.compt.keras.layers as layers\n')]