<jupyter_start><jupyter_text>Goal: given an input image $X=[x_1, x_2, ..., x_N]$ with values in $\mathbb{R}$, find a function f(x) so that - $f(x_i) \in [0,1] \ \forall i$ - $\sum_{i=1}^N{f(x_i)} = \mu N$ with $\mu=const$First idea: $f(x)=sigmoid(x)_c=\frac{1}{1+e^{-x-c}}$ and optimize for $c$.<jupyter_code>import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variable
import time
def newton(func, guess, threshold = 1e-7):
    guess = Variable(guess, requires_grad=True)
    value = func(guess)
    i = 0
    while abs(value.item()) > threshold:
        value = func(guess)
        value.backward()
        guess.data -= (value / guess.grad).data
        guess.grad.data.zero_()
        i += 1
    return guess.data, i
def sigmoidNormalize(X, c, min_value=0):
    return torch.sigmoid(X + c)*(1-min_value) + min_value
def getOptimizationFunction(X, target, min_value):
    def f(c):
        X2 = sigmoidNormalize(X, c, min_value)
        current = torch.mean(X2)
        return (current-target)
    return f
X = torch.randn(5, 5) * 10
print("Input:\n", X, sep='')
print("min:", torch.min(X), 'max:', torch.max(X), 'mean:', torch.mean(X))
print()

min_value = 0.1

initialC = torch.tensor(0.0)
print(initialC.shape)

sigX = sigmoidNormalize(X, initialC, min_value)
print("normalized:\n", sigX, sep='')
print("min:", torch.min(sigX), 'max:', torch.max(sigX), 'mean:', torch.mean(sigX))
print()

targetMean = 0.2
print("target mean:", targetMean)

startTime = time.monotonic()
finalC, iteration = newton(getOptimizationFunction(X.cuda(), targetMean, min_value), initialC.cuda())
endTime = time.monotonic()
print("Converged after", iteration, "iterations to an offset of", finalC)
print("Elapsed time:", (endTime-startTime)*1000, "ms")
print()

sigX = sigmoidNormalize(X, finalC, min_value)
print("normalized:\n", sigX, sep='')
print("min:", torch.min(sigX), 'max:', torch.max(sigX), 'mean:', torch.mean(sigX))
print()
def bisection(func, guess, step=1, threshold=1e-7):
    """
    Assumes func to be monotonic
    """
    
    value = func(guess).item()
    print("guess=%.5f -> value=%.5f"%(guess, value))
    if value > 0:
        right = guess
        right_value = value
        left = guess - step
        left_value = value
        while True:
            value = func(left).item()
            assert value < left_value, "function is not monotonic increasing"
            left_value = value
            print("left=%.5f -> left_value=%.5f"%(left, left_value))
            if left_value < 0:
                break
            step = step * 2
            left = guess - step
    else:
        left = guess
        left_value = value
        right = guess + step
        right_value = value
        while True:
            value = func(right).item()
            assert value > right_value, "function is not monotonic increasing"
            right_value = value
            print("right=%.5f -> right_value=%.5f"%(right, right_value))
            if right_value > 0:
                break
            step = step * 2
            right = guess + step
            
    i = 0
    while abs(value) > threshold:
        # false position method
        guess = (left * right_value - right * left_value) / (right_value - left_value)
        value = func(guess).item()
        print("guess=%.5f -> value=%.5f"%(guess, value))
        # keep left and right value
        if value > 0:
            right = guess
            right_value = value
        else:
            left = guess
            left_value = value
        i += 1
    return guess, i
X = torch.randn(5, 5) * 1
print("Input:\n", X, sep='')
print("min:", torch.min(X), 'max:', torch.max(X), 'mean:', torch.mean(X))
print()

min_value = 0.1

initialC = torch.tensor(-4)
print(initialC.shape)

sigX = sigmoidNormalize(X, initialC, min_value)
print("normalized:\n", sigX, sep='')
print("min:", torch.min(sigX), 'max:', torch.max(sigX), 'mean:', torch.mean(sigX))
print()

targetMean = 0.2
print("target mean:", targetMean)

startTime = time.monotonic()
finalC, iteration = bisection(getOptimizationFunction(X.cuda(), targetMean, min_value), initialC.cuda())
endTime = time.monotonic()
print("Converged after", iteration, "iterations to an offset of", finalC)
print("Elapsed time:", (endTime-startTime)*1000, "ms")
print()

sigX = sigmoidNormalize(X, finalC, min_value)
print("normalized:\n", sigX, sep='')
print("min:", torch.min(sigX), 'max:', torch.max(sigX), 'mean:', torch.mean(sigX))
print()<jupyter_output>Input:
tensor([[ 0.9823, -0.6003,  2.1897,  0.6543,  1.5350],
        [ 0.5103,  0.3197,  0.4895, -0.2049,  2.3913],
        [-0.2200,  0.9613,  0.3155, -0.7265, -0.4050],
        [ 2.5826,  0.9836,  1.0097,  1.0198, -0.5601],
        [ 0.6508,  0.2425, -0.4872,  0.5464,  0.2833]])
min: tensor(-0.7265) max: tensor(2.5826) mean: tensor(0.5785)

torch.Size([])
normalized:
tensor([[0.1420, 0.1090, 0.2265, 0.1306, 0.1705],
        [0.1266, 0.1221, 0.1261, 0.1132, 0.2501],
        [0.1130, 0.1411, 0.1220, 0.1079, 0.1109],
        [0.2756, 0.1420, 0.1431, 0.1435, 0.1093],
        [0.1305, 0.1205, 0.1100, 0.1276, 0.1214]])
min: tensor(0.1079) max: tensor(0.2756) mean: tensor(0.1414)

target mean: 0.2
guess=-4.00000 -> value=-0.05859
right=-3.00000 -> right_value=-0.00249
right=-2.00000 -> right_value=0.10328
guess=-3.27610 -> value=-0.02220
guess=-3.05029 -> value=-0.00635
guess=-2.98941 -> value=-0.00166
guess=-2.97379 -> value=-0.00042
guess=-2.96984 -> value=-0.00011
guess=-2.96884 -> valu[...]
{'fraction_non_alphanumeric': 0.17460894870862131, 'fraction_numerical': 0.08075663877773735, 'mean_word_length': 2.864371047083626, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '599095', 'license': 'MIT', 'n_tokens_mistral': 2285, 'n_tokens_neox': 2050, 'n_words': 471, 'path': 'network/tests/SigmoidOptimization.ipynb', 'repo_name': 'shamanDevel/AdaptiveSampling', 'size': '5498'}