//  [python]
def compute_candidate_locations(args, corpus, cache_filename, model,
        segmenter, classnum, second_sample):
    # Phase 2.  Identify a set of candidate locations for doorways.
    # Place the median doorway activation in every location of an image
    # and identify where it can go that doorway pixels increase.
    if all(k in corpus for k in ['candidate_indices',
            'candidate_sample', 'candidate_score',
            'candidate_location', 'object_score_at_candidate',
            'candidate_location_popularity']):
        return
    progress = default_progress()
    feature_shape = model.feature_shape[args.layer][2:]
    num_locations = numpy.prod(feature_shape).item()
    with torch.no_grad():
        # Simplify - just treat all locations as possible
        possible_locations = numpy.arange(num_locations)

        # Speed up search for locations, by weighting probed locations
        # according to observed distribution.
        location_weights = (corpus.object_location_popularity).double()
        location_weights += (location_weights.mean()) / 10.0
        location_weights = location_weights / location_weights.sum()

        candidate_scores = []
        object_scores = []
        prng = numpy.random.RandomState(1)
        for [zbatch] in progress(
                torch.utils.data.DataLoader(TensorDataset(second_sample),
                batch_size=args.inference_batch_size, num_workers=10,
                pin_memory=True),
                desc="Candidate pool"):
            batch_scores = torch.zeros((len(zbatch),) + feature_shape).cuda()
            flat_batch_scores = batch_scores.view(len(zbatch), -1)
            zbatch = zbatch.cuda()
            tensor_image = model(zbatch)
            segmented_image = segmenter.segment_batch(tensor_image,
                    downsample=2)
            mask = (segmented_image == classnum).max(1)[0]
            object_score = torch.nn.functional.adaptive_avg_pool2d(
                    mask.float(), feature_shape)
            baseline_presence = mask.float().view(mask.shape[0], -1).sum(1)

            edit_mask = torch.zeros((1, 1) + feature_shape).cuda()
            if '_tcm' in args.variant:
                # variant: top-conditional-mean
                replace_vec = (corpus.mean_present_feature
                        [None,:,None,None].cuda())
            else: # default: weighted mean
                replace_vec = (corpus.weighted_mean_present_feature
                        [None,:,None,None].cuda())
            # Sample 10 random locations to examine.
            for loc in prng.choice(possible_locations, replace=False,
                    p=location_weights, size=5):
                edit_mask.zero_()
                edit_mask.view(-1)[loc] = 1
                model.edit_layer(args.layer,
                        ablation=edit_mask, replacement=replace_vec)
                tensor_image = model(zbatch)
                segmented_image = segmenter.segment_batch(tensor_image,
                    downsample=2)
                mask = (segmented_image == classnum).max(1)[0]
                modified_presence = mask.float().view(
                        mask.shape[0], -1).sum(1)
                flat_batch_scores[:,loc] = (
                        modified_presence - baseline_presence)
            candidate_scores.append(batch_scores.cpu())
            object_scores.append(object_score.cpu())

        object_scores = torch.cat(object_scores)
        candidate_scores = torch.cat(candidate_scores)
        # Eliminate candidates where the object is present.
        candidate_scores = candidate_scores * (object_scores == 0).float()
        candidate_score_at_image, candidate_location_in_image = (
                candidate_scores.view(args.search_size, -1).max(1))
        best_candidate_scores, best_candidate_images = torch.sort(
                -candidate_score_at_image)
        all_candidate_indices = torch.sort(
                best_candidate_images[:(args.train_size+args.eval_size)])[0]
        corpus.candidate_indices = all_candidate_indices[:args.train_size]
        corpus.candidate_sample = second_sample[corpus.candidate_indices]
        corpus.candidate_location = candidate_location_in_image[
                corpus.candidate_indices]
        corpus.candidate_score = candidate_score_at_image[
                corpus.candidate_indices]
        corpus.object_score_at_candidate = object_scores.view(
                len(object_scores), -1)[
                corpus.candidate_indices, corpus.candidate_location]
        corpus.candidate_location_popularity = torch.bincount(
            corpus.candidate_location,
            minlength=num_locations)
        corpus.eval_candidate_indices = all_candidate_indices[
                -args.eval_size:]
        corpus.eval_candidate_sample = second_sample[
                corpus.eval_candidate_indices]
        corpus.eval_candidate_location = candidate_location_in_image[
                corpus.eval_candidate_indices]
    numpy.savez(cache_filename, **corpus)