Renders two aligned Activation Atlases of the given models' layers.

    Returns a generator of the two atlasses, and a nested generator for intermediate
    atlasses while they're being rendered.

def aligned_activation_atlas(
    model1,
    layer1,
    model2,
    layer2,
    grid_size=10,
    icon_size=80,
    num_steps=1024,
    whiten_layers=True,
    number_activations=NUMBER_OF_AVAILABLE_SAMPLES,
    icon_batch_size=32,
    verbose=False,
):
    """Renders two aligned Activation Atlases of the given models' layers.

    Returns a generator of the two atlasses, and a nested generator for intermediate
    atlasses while they're being rendered.
    """
    combined_activations = _combine_activations(
        layer1, layer2, number_activations=number_activations
    )
    layouts = aligned_umap(combined_activations, verbose=verbose)

    for model, layer, layout in zip((model1, model2), (layer1, layer2), layouts):
        directions, coordinates, densities = bin_laid_out_activations(
            layout, layer.activations[:number_activations, ...], grid_size, threshold=10
        )

        def _progressive_canvas_iterator():
            icons = []
            for directions_batch in chunked(directions, icon_batch_size):
                icon_batch, losses = render_icons(
                    directions_batch,
                    model,
                    alpha=False,
                    layer=layer.name,
                    size=icon_size,
                    n_steps=num_steps,
                    S=layer_inverse_covariance(layer) if whiten_layers else None,
                )
                icons += icon_batch
                yield make_canvas(icons, coordinates, grid_size)

        yield _progressive_canvas_iterator()