aboutsummaryrefslogtreecommitdiffstats
path: root/python/optimization.py
blob: 15fd0ec8215bdf102c47a20c809e2fc7722301ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import numpy as np
import numpy.linalg as la
from scipy.interpolate import PchipInterpolator

def decr_fun(a, b):
    return np.sqrt(np.square(a).sum() + np.square(b).sum())

def KLfit(P, q, b):
    """
    solves for x, min KL(x, q)
                  s.t. Px = b
                       x>=0
                       sum(x) = 1
    """
    alpha = 0.4
    beta = 0.8
    if(len(P.shape)==1):
        P = P[None]
    P = np.vstack([P, np.ones(P.shape[1])])
    b = np.hstack([b, 1])
    #init x and nu
    x = np.ones(P.shape[1])/P.shape[1]
    nu = np.ones(P.shape[0])
    decr = np.Inf
    eps = 1e-12
    niter = 1
    while decr > eps and niter < 500:
        rdual = 1 + np.log(x) - np.log(q) + P.T.dot(nu)
        rprimal = P.dot(x) - b
        S = -P.dot((x*P).T)
        Dnu_pd = la.solve(S, (P*x).dot(rdual) - rprimal)
        Dx_pd = -x*(P.T.dot(Dnu_pd)+rdual)
        #backtracking search
        phi = 1
        newx = x + phi * Dx_pd
        newnu = nu + phi * Dnu_pd

        while sum(newx<0) > 0:
            phi *= beta
            newx = x + phi * Dx_pd
            newnu = nu + phi * Dnu_pd

        newrdual = 1 + np.log(newx) - np.log(q) + P.T.dot(newnu)
        newrprimal = P.dot(newx) - b

        while decr_fun(newrdual, newrprimal) > (1 - alpha * phi) * decr_fun(rdual, rprimal):
            phi *= beta
            newx = x + phi * Dx_pd
            newnu = nu + phi * Dnu_pd
            newrdual = 1 + np.log(newx) - np.log(q) + P.T.dot(newnu)
            newrprimal = P.dot(newx) - b

        x = newx
        nu = newnu
        decr = decr_fun(newrdual, newrprimal)
        niter += 1

    return {"obj": sum(x*(np.log(x) - np.log(q))), "weight":x, "status": decr}

def interpweights(w, v1, v2):
    """ Given L=(w, v1), computes neww such that newL=(neww, v2) = L in distribution"""
    cumw = np.cumsum(w)
    neww = PchipInterpolator(v1, cumw)(v2, der = 1)
    interpweights = new/np.sum(neww)
    return interpweights

def interpvalues(w, v, neww):
    """ Given a distribution D=(w,v), compute new weights
    such that Dnew=(neww, newv) equals D in distribution """
    cumw = np.cumsum(w)
    cdf = PchipInterpolator(v, cumw)
    eps = 1e-4
    neww = np.zeros(neww.size)
    cumneww = np.cumsum(neww)
    mid = 0
    for i in range(neww.size):
        iter = 0
        hi = cdf(1)
        lo = mid
        if hi < cumneww[i]:
            neww[i] = hi
            continue
        if cdf(lo) > cumneww[i]:
            neww[i] = lo
            continue
        mid = (lo+hi)/2
        iter = 0
        while( abs(cdf(mid)-cumneww[i]) > eps ):
            if cdf(mid) > cumneww[i]:
                hi = mid
            else:
                lo = mid
            mid = (lo + hi)/2
        newv[i] = mid
    return newv


if __name__=="__main__":
    #write some small test
    P = np.array([[5, 4, 3, 2, 1],[3, 3, 4, 3, 3]])
    w = np.ones(5)/5
    b = np.array([2.5, 3.3])
    test = KLfit(P, w, b)