def clean_indeces(indeces, obj):
    new_indeces = []
    for i, index in enumerate(indeces):
        if isinstance(index, slice):
            if index.step not in [1, None]:
                raise Exception(
                    'Supports slices with only step 1. Generate a list otherwise.'
                )
            start = index.start if index.start is not None else 0
            stop = index.stop if index.stop is not None else obj.indeces[
                i].length
            dummy_index = op.ArrayIndex('di_{}'.format(
                remove_chars(datetime.now())),
                                        range=(start, stop))
            index = dummy_index
        elif isinstance(index, Iterable):
            index_list = op.OpArray(index)
            dummy_index = sp.Idx('di_' + remove_chars(datetime.now()),
                                 range=len(index))
            index = index_list[dummy_index]
        elif isinstance(index, op.OpSet):
            dummy_index = sp.Idx('di_' + remove_chars(datetime.now()),
                                 range=index.length)
            index = index[dummy_index]
        # else:
        # 	raise Exception('Unrecognized format for index.')
        new_indeces.append(index)
    return new_indeces
Exemple #2
0
    def generate(self, generator):
        """
        generate periodic conditions functions for loo.py backend.
        """
        self.generator = generator

        def set_order(array, remove_index=None):
            out = [-1] * len(self.sorder)
            for i, s in enumerate(self.sorder):
                out[s] = array[i]
            if remove_index:
                out.pop(self.sorder[remove_index])
            return out

        nx, ny, nz, nv = sp.symbols('nx, ny, nz, nv', integer=True)
        shape = set_order([nv, nx, ny, nz])

        i = sp.Idx('i', (0, nx))
        j = sp.Idx('j', (0, ny))
        k = sp.Idx('k', (0, nz))
        s = sp.Idx('s', (0, nv))

        fi = sp.IndexedBase('f', shape)  #pylint: disable=invalid-name
        f_store = sp.Matrix(
            [fi[set_order([s, 0, j, k])], fi[set_order([s, nx - 1, j, k])]])
        f_load = sp.Matrix(
            [fi[set_order([s, nx - 2, j, k])], fi[set_order([s, 1, j, k])]])
        iloop = set_order([s, i, j, k], remove_index=1)
        generator.add_routine(('update_x', For(iloop, sp.Eq(f_store, f_load))))

        if len(self.sorder) > 2:
            f_store = sp.Matrix([
                fi[set_order([s, i, 0, k])], fi[set_order([s, i, ny - 1, k])]
            ])
            f_load = sp.Matrix([
                fi[set_order([s, i, ny - 2, k])], fi[set_order([s, i, 1, k])]
            ])
            iloop = set_order([s, i, j, k], remove_index=2)
            generator.add_routine(
                ('update_y', For(iloop, sp.Eq(f_store, f_load))))

        if len(self.sorder) > 3:
            f_store = sp.Matrix([
                fi[set_order([s, i, j, 0])], fi[set_order([s, i, j, nz - 1])]
            ])
            f_load = sp.Matrix([
                fi[set_order([s, i, j, nz - 2])], fi[set_order([s, i, j, 1])]
            ])
            iloop = set_order([s, i, j, k], remove_index=3)
            generator.add_routine(
                ('update_z', For(iloop, sp.Eq(f_store, f_load))))
Exemple #3
0
    def generate(self, sorder):
        """
        Generate the numerical code.

        Parameters
        ----------
        sorder : list
            the order of nv, nx, ny and nz
        """
        from pylbm.generator import For
        from pylbm.symbolic import nx, ny, nz, indexed, ix

        ns = int(self.stencil.nv_ptr[-1])
        dim = self.stencil.dim

        istore, iload, ncond = self._get_istore_iload_symb(dim)
        rhs, _ = self._get_rhs_dist_symb(ncond)

        idx = sp.Idx(ix, (0, ncond))
        fstore = indexed('f', [ns, nx, ny, nz],
                         index=[istore[idx, k] for k in range(dim + 1)],
                         priority=sorder)
        fload = indexed('f', [ns, nx, ny, nz],
                        index=[iload[0][idx, k] for k in range(dim + 1)],
                        priority=sorder)

        self.generator.add_routine(
            ('slip_xodd', For(idx, sp.Eq(fstore, -fload + rhs[idx]))))
Exemple #4
0
def pde_model_disc(N, dx):
    if dx < 0:
        raise ValueError("dx must be non-negative")

    if N < 1:
        raise ValueError("N must be positive")

    x = sympy.Symbol('x')
    r = sympy.Symbol('r')
    cl = sympy.Symbol('cl')
    L = sympy.Symbol('L')

    i = sympy.Idx('i')
    c = sympy.IndexedBase('c')
    m = model.Model()
    m.name = 'laplace discretised'
    m.parameters = {cl, L}
    m.solution_variables = {c[i]}
    m.bounds = sympy.And(i >= 0, i <= N)
    m.eqs = {
        (sympy.And(i > 0, i < N), sympy.Eq((c[i+1] - 2*c[i] - c[i-1])/(dx**2), 0)),
        (sympy.Eq(i, 0), sympy.Eq(c[i], cl)),
        (sympy.Eq(i, N), sympy.Eq((c[i] - c[i-1])/dx, 0))
    }

    return m
Exemple #5
0
def space_loop(ranges, permutation=None):
    if not permutation:
        permutation = range(len(ranges) + 1)

    indices = [ix, iy, iz]
    idx = []
    for ir, r in enumerate(ranges):  #pylint: disable=invalid-name
        idx.append(sp.Idx(indices[ir], r))
    return set_order([0] + idx, permutation, remove_ind=[0])
Exemple #6
0
def kk_ver1(Pos, SpCons, Len, eps=0.001):
    '関数リストを用いた部分最適化'
    t_start = time()
    nodes, dim = Pos.shape
    const = (SpCons, Len)
    P = sp.IndexedBase('P')
    K = sp.IndexedBase('K')
    L = sp.IndexedBase('L')
    i, j, d = [
        sp.Idx(*spec) for spec in [('i', nodes), ('j', nodes), ('d', dim)]
    ]
    i_range, j_range, d_range = [(idx, idx.lower, idx.upper)
                                 for idx in [i, j, d]]

    #potential functionの用意
    dist = sp.sqrt(sp.Sum((P[i, d] - P[j, d])**2, d_range))
    Potential = 1 / 2 * K[i, j] * (dist - L[i, j])**2
    E = sp.Sum(Potential, i_range, j_range).doit()

    #list of functions
    E_jac, E_hess = [], []
    for m in range(nodes):
        variables = [P[m, d] for d in range(dim)]
        mth_jac, mth_hess = [
            partial(sp.lambdify((K, L, P), f, dummify=False), *const) for f in
            [sp.Matrix([E]).jacobian(variables),
             sp.hessian(E, variables)]
        ]
        E_jac.append(mth_jac)
        E_hess.append(mth_hess)
        print('generating...', int(m / nodes * 100), '%', "\r", end="")

    print('derivative functions are generated:', time() - t_start, 's')

    ##Optimisation
    delta_max = sp.oo
    loops = 0
    while (delta_max > eps):
        max_idx, delta_max = 0, 0
        for m in range(nodes):
            mth_jac = E_jac[m]
            delta = la.norm(mth_jac(Pos))
            if (delta_max < delta):
                delta_max = delta
                max_idx = m

        print(loops, 'th:', max_idx, ' delta=', delta_max)
        loops += 1
        jac = E_jac[max_idx]
        hess = E_hess[max_idx]
        while (la.norm(jac(Pos)) > eps):
            delta_x = la.solve(hess(Pos), jac(Pos).flatten())
            Pos[max_idx] -= delta_x

    print('Fitting Succeeded')
    print('Finish:', time() - t_start, 's')
    return Pos
Exemple #7
0
def can_i_concat_indexed_objects():
    import sympy as sym

    # First indexed object
    n = sym.symbols('n', integer=True, negative=False)
    X = sym.IndexedBase('X')
    i = sym.Idx('i', (0, n - 1))

    # Second indexed object
    m = sym.symbols('m', integer=True, negative=False)
    Y = sym.IndexedBase('Y')
    j = sym.Idx('j', (0, m - 1))

    # I would like to create a third object, Z that concatentates X and Y, s.t.
    Z = sym.IndexedBase('Z')
    k = sym.Idx('k', (0, n + m - 1))

    # It should satisfy
    sym.Eq(Z[i], X[i])  # the first elements are from X
    sym.Eq(Z[j + n], Y[j])  # the last elements are from Y

    # which means, given
    sum_X = sym.summation(X[i], (i, 0, n - 1))
    sum_Y = sym.summation(Y[j], (j, 0, m - 1))

    sum_Z = sym.summation(Z[k], (k, 0, n - 1))
    sum_Z_part1 = sym.summation(Z[k], (k, 0, n - 1))
    sum_Z_part2 = sym.summation(Z[k], (k, n, n + m - 1))

    sym.Eq(sum_Z, sum_X + sum_Y)
    sym.Eq(sum_Z_part1, sum_X)
    sym.Eq(sum_Z_part2, sum_Y)

    # One way to do this is
    Z_k = sym.Piecewise((X[k], k < n), (Y[k - n], k >= n))
    sum_Z = sym.summation(Z_k, (k, 0, n - 1))

    # But this is not subscriptable
    (sum_Z - sum_X - sum_Y).simplify()
Exemple #8
0
def kk_ver2(Pos, SpCons, Len):
    '2n変数の一括最適化'
    t_start = time()
    nodes, dim = Pos.shape
    const = (SpCons, Len)
    P = sp.IndexedBase('P')
    K = sp.IndexedBase('K')
    L = sp.IndexedBase('L')
    X = sp.IndexedBase('X')

    i, j, d = [
        sp.Idx(*spec) for spec in [('i', nodes), ('j', nodes), ('d', dim)]
    ]
    i_range, j_range, d_range = [(idx, idx.lower, idx.upper)
                                 for idx in [i, j, d]]

    #potential functionの用意
    print('reserving Potential function')
    dist = sp.sqrt(sp.Sum((P[i, d] - P[j, d])**2, d_range)).doit()
    E = sp.Sum(K[i, j] * (dist - L[i, j])**2, i_range, j_range)

    #jacobian,Hessianの用意
    print('reserving jacobian and hessian')
    varP = [P[i, d] for i in range(nodes) for d in range(dim)]
    E_jac = sp.Matrix([E]).jacobian(varP)
    E_hes = sp.hessian(E, varP)

    print('generating derivative equation')
    varX = [X[i] for i in range(nodes * dim)]
    PX = np.array([X[i * dim + j] for i in range(nodes)
                   for j in range(dim)]).reshape(nodes, dim)

    E_X = E.replace(K, SpCons).replace(L, Len).replace(P, PX).doit()
    E_jac_X = sp.Matrix([E_X]).jacobian(varX)
    E_hes_X = sp.hessian(E_X, varX)

    print('generating derivative function')
    F, G, H = [sp.lambdify(X, f) for f in [E_X, E_jac_X, E_hes_X]]

    print('fitting')
    res = minimize(F,
                   Pos,
                   jac=lambda x: np.array([G(x)]).flatten(),
                   hess=H,
                   method='trust-ncg')

    print('[time:', time() - t_start, 's]')
    return res.x.reshape(nodes, dim)
Exemple #9
0
def space_idx(ranges, priority=None):
    """
    Return a list of SymPy Idx with the bounds of the ranges.
    This list can be permuted if priority is defined.

    Parameters
    ----------

    ranges : list
        bounds of the range for each created SymPy Idx

    priority : list
        define how to reorder the ranges (lower to greater)
            (default is None)

    Return
    ------

    list
        list of SymPy Idx with the right ranges ordered by priority

    Examples
    --------

    >>> loop = space_idx([(0, 10), (-10, 10)])
    >>> loop
    [ix_, iy_]
    >>> loop[0].lower, loop[0].upper
    (0, 10)
    >>> loop = space_idx([(0, 10), (-10, 10)], priority=[1, 0])
    >>> loop
    [iy_, ix_]
    >>> loop[0].lower, loop[0].upper
    (-10, 10)

    """
    indices = [ix_, iy_, iz_]

    idx = []
    for ir, r in enumerate(ranges):  #pylint: disable=invalid-name
        idx.append(sp.Idx(indices[ir], r))

    if priority:
        return set_order(idx, priority)
    else:
        return idx
Exemple #10
0
def kk_ver3(Pos, SpCons, Len, eps=0.00001):
    '頂点の匿名性から微分関数を1つにまとめたもの'
    start = time()
    nodes, dim = Pos.shape
    ni = nodes - 1
    X = sp.IndexedBase('X')  # 動かす頂点
    P = sp.IndexedBase('P')  # 動かさない頂点
    Ki = sp.IndexedBase('Ki')  # 動かす頂点に関するばね定数
    Li = sp.IndexedBase('Li')  # 動かす頂点に関する自然長

    j, d = [sp.Idx(*spec) for spec in [('j', ni), ('d', dim)]]
    j_range, d_range = [(idx, idx.lower, idx.upper) for idx in [j, d]]

    #potential functionの用意
    dist = sp.sqrt(sp.Sum((X[d] - P[j, d])**2, d_range)).doit()
    Ei = sp.Sum(Ki[j] * (dist - Li[j])**2, j_range)

    #jacobian,Hessianの用意
    varX = [X[d] for d in range(dim)]
    Ei_jac, Ei_hess = [
        sp.lambdify((X, P, Ki, Li), sp.simplify(f), dummify=False)
        for f in [sp.Matrix([Ei]).jacobian(varX),
                  sp.hessian(Ei, varX)]
    ]
    print('generate function:', time() - start, 's')

    start = time()
    ##Optimisation
    delta_max = sp.oo
    xpkl = partial(_xpkl, P=Pos, K=SpCons, L=Len, n=nodes)
    while (delta_max > eps):
        # 最も改善すべき頂点の選択
        norms = np.array(
            list(map(lambda m: la.norm(Ei_jac(*xpkl(m))), range(nodes))))
        max_idx, delta_max = norms.argmax(), norms.max()

        # Newton法で最適化
        xpkl_m = xm, pm, km, lm = xpkl(max_idx)
        while (la.norm(Ei_jac(*xpkl_m)) > eps):
            delta_x = la.solve(Ei_hess(*xpkl_m), Ei_jac(*xpkl_m).flatten())
            xm -= delta_x

    print('Finish:', time() - start, 's')
    return Pos
Exemple #11
0
def __kk2__():
    ni = n - 1
    P = sp.IndexedBase('P', shape=(ni, dim))  #ni個のPositions
    L = sp.IndexedBase('L')  #L[j] is Natural Lens between p and P[j]
    K = sp.IndexedBase('K')  #K[j] is p,P[j]間のばね定数
    p = sp.IndexedBase('p')  #Position of one of Pで動かす点
    q = sp.IndexedBase('q')  #最適化の式の解

    i, j, d, x = [
        sp.Idx(*spec)
        for spec in [('i', ni), ('j', ni), ('d', dim), ('x', dim)]
    ]
    i_range, j_range = [(idx, idx.lower, idx.upper) for idx in [i, j]]
    d_range = (d, d.lower, d.upper)

    distance_j = sp.sqrt(sp.Sum((p[d] - P[j, d]) * (p[d] - P[j, d]), d_range))
    Energy_j = (K[j] * (distance_j - L[j]) * (distance_j - L[j]) / 2)
    Energy = (sp.Sum(Energy_j, j_range))

    variables = [p[d] for d in range(dim)]
    jac_sp, hess_sp = [
        sp.Matrix([Energy]).jacobian(variables),
        sp.hessian(Energy, variables)
    ]
    jac, hess = [(sp.lambdify((K, L, P, p), f, dummify=False))
                 for f in [jac_sp, hess_sp]]

    #Sum(jac_sp[d]**2,d_range)の代わり
    abs_grad_E_sp = 0
    for i in range(dim):
        abs_grad_E_sp += jac_sp[i] * jac_sp[i]
    abs_grad_E_sp = sp.sqrt(abs_grad_E_sp)

    abs_grad_E = sp.lambdify((K, L, P, p), abs_grad_E_sp, dummify=False)

    markdown(
        r'''
#kk2(Pos,Lens,Cons,dim=2,eps=0.000001)
グラフ描画の初期配置:Pos、ばね定数:Cons、ばねの自然長:Lensをもらい、エネルギーが最小(初期配置によっては極小)の近似的な配置を返します。

1.動かす点:$p$を一つとり、Pos,Cons,Lensから点$p$を除いたものを$P,K,L$と置く。

2.動かす点$p$に対し、そのエネルギー:$E$,その勾配の絶対値:$\left|gradE\right|$、ヤコビアン:$J$、ヘシアン$H$を以下のよう定義する。
$$E={Energy}$$
$$\left|gradE\right|={abs_grad_E_sp}$$
$$J={jac_sp}$$
$$H={hess_sp}$$

3.動かす点:$p$を変えながら、$\left|gradE\right|$が最大となる点探す。

4.$p$を以下の手順で最適化する。

	1.$x$を変数として以下の連立方程式を解く。
$$H x=J$$

	2.その解を用いて、点$p$を更新する。
$$p=p-x$$

5.動かす点:$p$を変えながら、$\left|gradE\right|$が最大となる点探し、$\left|gradE\right|>eps$ならば4に戻る。


		''', **locals())

def ensure_reaction(r):
    if isinstance(r, Reaction): return r
    return reaction_registry[r]

def ensure_cooling(c):
    if isinstance(c, CoolingAction): return c
    return cooling_registry[c]

def ensure_species(s):
    if isinstance(s, Species): return s
    return species_registry[s]

count_m = sympy.Symbol('m', integer=True)
index_i = sympy.Idx('i', count_m)

class ReactionCoefficient(sympy.Symbol):
    def _eval_derivative(self, s):
        if s == self.energy:
            return sympy.Symbol("r%s" % self)
        else:
            return super(ReactionCoefficient, self)._eval_derivative(s)

    energy = None

class Reaction(ComparableMixin):
    def __init__(self, name, coeff_fn, left_side, right_side):
        self.name = name
        self.coeff_fn = coeff_fn
        #self.coeff_sym = sympy.IndexedBase(name, (count_m,))
Exemple #13
0
import sympy

N, M = sympy.symbols('N, M')
x = sympy.IndexedBase('X', shape=(N, M))
i, j = (sympy.Idx('i', 2), sympy.Idx('j', 2))
b = -x[i, j]
sympy.printing.print_ccode(b)


def grad(V):
    V = sympy.sympify(V)
    t = sympy.symbols('t')
    syms = sorted(V.free_symbols, key=sympy.Symbol.sort_key)
    X = sympy.MatrixSymbol('_X', len(syms), 1)
    subs = [(s, X[i, 0]) for (i, s) in enumerate(syms)]
    dVs = [sympy.diff(V, s).subs(subs) for s in syms]
    return sympy.lambdify((t, X), sympy.Matrix(dVs), 'numpy', dummify=False)


#    return lambdastr((t, X), sympy.Matrix(dVs), dummify=False)
Exemple #14
0
def kk2(Pos, Lengthes, Cons, dim=dim, n=n, eps=0.000001):
    P = sp.IndexedBase('P', shape=(dim, n))  #n個のPositions
    L = sp.IndexedBase('L')  #L[i,j] is Natural Lengthes between pi and pj
    K = sp.IndexedBase('K')  #K[i,j] is ij間のばね定数
    p = sp.IndexedBase('p')  #Position of one of P

    i, j, d, x = [
        sp.Idx(*spec) for spec in [('i', n), ('j', n), ('d', dim), ('x', dim)]
    ]
    i_range, j_range, d_range = [(idx, idx.lower, idx.upper)
                                 for idx in [i, j, d]]

    distance_ij = sp.sqrt(sp.Sum((P[d, i] - P[d, j])**2, d_range)).doit()
    Energy_ij = (K[i, j] * (distance_ij - L[i, j]) * (distance_ij - L[i, j]) /
                 2)
    Potential_i = sp.Sum(Energy_ij, j_range)
    print(Potential_i)
    Potential_i_lambda = sp.lambdify((i, P, L, K), Potential_i)
    #	print(Potential_i_lambda(1,Pos,Lengthes,Cons))
    #	total_energy = (sp.Sum(Potential_i,i_range).doit())/2 #使わない

    delta_fomula_i = sp.sqrt(sp.Sum(sp.diff(Potential_i, P[d, i])**2, d_range))
    print('delta_fomula_i{}', delta_fomula_i)
    delta_fomula_i_lambda = lambda i, P, K, L: 0
    #	delta_fomula_i_lambda = sp.lambdify((i,P,L,K),delta_fomula_i,modules=mymodules)

    #変位
    __diffx__ = sp.diff(Potential_i, P[x, i])
    print({}, __diffx__)
    delta_x_lambda = lambda x, p: 1
    #	delta_x_lambda = (lambda x,p:sp.lambdify((x,p,P,L,K),sp.Sum(sp.diff(__diffx__,P[d,i])*p[d],d_range)+__diffx__,modules=mymodules)(x,p,Pos,Lengthes,Cons))
    max_delta = 0
    max_delta_number = 0

    markdown(
        r'''
$$distanceij={distance_ij}$$
$$Energy_ij={Energy_ij}$$
$$Potential_ij={Potential_i}$$
$$delta_fomula_i={delta_fomula_i}$$
''', **locals())

    #serchmax
    for s in range(n):
        if (delta_fomula_i_lambda(s, Pos, Lengthes, Cons) > max_delta):
            max_delta = delta_fomula_i_lambda(s, Pos, Lengthes, Cons)
            max_delta_number = s
    #P[d,mux_delta_number]を最適化
    while max_delta > eps:
        print('{0}'.format(max_delta_number))
        while delta_fomula_i_lambda(max_delta_number, Pos, Lengthes,
                                    Cons) > eps:
            s = sp.solve([delta_x_lambda(x, p) for x in range(dim)],
                         [p[x] for x in range(x)]).doit()
            for d in range(dim):
                Pos[d, i] -= s[p[d]]

        #serch max
        max_delta = 0
        max_delta_number = 0
        for i in range(n):
            if (delta_fomula_i_lambda(i, Pos, Lengthes, Cons) > max_delta):
                max_delta = delta_fomula_i_lambda(i, Pos, Lengthes, Cons)
                max_delta_number = i

    return Pos
Exemple #15
0
def _kamada_kawai_intro_(dim=2):
    markdown(r'''
#力学モデルを用いたグラフ描画

グラフを描画する際、頂点の配置をどのようにするかということは視覚的な理解に大きな影響を及ぼす。
本記事では、グラフの頂点と辺に仮想的な力を割り当て、力学的エネルギーの低い安定状態を探して
グラフのレイアウトを求める**kamada-kawai**法を用いる。
''')

    P = sp.IndexedBase('P')
    K = sp.IndexedBase('k')
    L = sp.IndexedBase('l')

    n = sp.Symbol('n', integer=True)

    i, j, d = [sp.Idx(*spec) for spec in [('i', n), ('j', n), ('d', dim)]]
    i_range, j_range, d_range = [(idx, idx.lower, idx.upper)
                                 for idx in [i, j, d]]

    #potential functionの用意
    dist = sp.sqrt(sp.Sum((P[i, d] - P[j, d])**2, d_range)).doit()
    Potential = K[i, j] * (dist - L[i, j])**2 / 2
    E = sp.Sum(Potential, i_range, j_range) / 2

    P_id, k_ij, l_ij, d_ij = ['P_{i,d}', 'k_{i,j}', 'l_{i,j}', 'd_{i,j}']

    markdown(
        r'''
##力学的エネルギーの定義

まず、グラフの全頂点がばねで結ばれていると仮定すると、系全体の力学的エネルギーは次で表される。
$$E = {E}$$
ただし、${P_id}$はi番目の頂点の座標の第$j$成分、${k_ij},{l_ij}$はそれぞれ頂点$i$と頂点$j$の間のばね定数、自然長とする。

${k_ij},{l_ij}$は、${d_ij}$を頂点$i$と頂点$j$を結ぶ最短経路の長さとして
$${k_ij} = K / {d_ij}^2 (i \neq j) \ \  0(i = j)$$
$${l_ij} = L \times {d_ij}$$
で定める($K,L$は定数)。

${d_ij}$は**Warshall-Floyd**のアルゴリズムにより求めることができる。
''', **locals())

    nd = n * dim
    _E = sp.Function('E')

    x = sp.IndexedBase('x')
    m = sp.Symbol('m', integer=True)
    i0 = sp.Idx('i', (1, n))
    i0_range = (i0, i0.lower, i0.upper)

    var0 = [x[d] for d in range(dim)]
    var0_m = sp.Matrix([var0])
    var0_Pm = sp.Matrix([[P[m, d] for d in range(dim)]])

    dist0 = sp.sqrt(sp.Sum((P[i0, d] - x[d])**2, d_range)).doit()
    E0 = sp.Sum(K[i0, 0] * (dist0 - L[i0, 0])**2 / 2, i0_range)
    E0_jac = sp.simplify(sp.Matrix([E0]).jacobian(var0))
    E0_hess = sp.simplify(sp.hessian(E0, var0))

    delta_x = sp.IndexedBase("\Delta x")
    delta_x_vec = sp.Matrix([[delta_x[d] for d in range(dim)]])
    norm = sp.sqrt(sp.Sum(sp.diff(_E(P[i, d]), P[i, d])**2, d_range).doit())

    markdown(
        r'''
##エネルギーの最小化

例として、頂点数が{n},次元が{dim}であるときを考える。
力学的エネルギーが最小になる点では、$gradE = \vec 0$が成り立つ。すなわち、変数が${nd}$個ある${nd}$本の非線型連立方程式を解けばよいのだが、これを解析的に解くのは難しい。
そこで、次のような方法で近似解を求める。

1:まず、特定の頂点1つに着目し、他の頂点の位置を固定する。

2:そして、Newton-Raphson法により選んだ頂点の座標について力学的エネルギー$E$の最小化を行う。

3:着目する頂点を変えて1,2を繰り返す。

4:$\|gradE\|$が十分小さくなったら終了、その時の座標の値を解とする。

以下で、その具体的な方法について述べる。

##近似解の導出

選んだ頂点をmとし、その座標を$P_m = {var0_m}$とする。つまり${var0_Pm} = {var0_m}$である。

このときNewton-Raphson法による反復式は、変数を${var0_m}$としたときのEの1次導関数を$J_m$、2次導関数を$H_m$として
$$H_m {delta_x_vec.T} = -J_m$$
により表される。
これは{dim}元連立1次方程式となり容易に解けて変位$\Delta P_i = {delta_x_vec}$が求められるので、$P_i = P_i + \Delta P_i$により座標を更新する。
以上を繰り返し、変位が十分小さくなったら操作を終了する。

例えば、$m=0$だとすると、反復式は
$${E0_hess}{delta_x_vec.T} = -{E0_jac.T} $$
となる。

選んだ頂点の最適化が終わったら、別な頂点を選んで上記の最適化を繰り返す。
$max_i {norm}$が十分小さくなったら更新を終了してその時の座標を力学的エネルギー$E$が最小となる解とする。
''', **locals())
Exemple #16
0
 def indexed_samples(var_name, idx_name, nobs_name):
     nobs = sym.symbols(nobs_name, integer=True, negative=False)
     var = sym.IndexedBase(var_name)
     idx = sym.Idx(idx_name,
                   (0, nobs - 1))  # an index ranging from 0 to nobs-1
     return var, idx, nobs
Exemple #17
0
def symbolic_population_combo_stats():
    import sympy as sym

    class Constraints(list):
        def __setitem__(self, key, val):
            if not isinstance(key, tuple):
                key = (key, '')
            self.append((key, val))

        def __getitem__(self, key):
            if isinstance(key, tuple):
                found = [v for k, v in self if k == key][0]
            else:
                found = [v for (a, lbl), v in self if a == key]
            return found

        def get_subs(self, *args):
            found = []
            for k in args:
                vs = self[k]
                for v in vs:
                    found.append((k, v))
            return found

    constraints = Constraints()

    # First indexed object
    n = sym.symbols('n', integer=True, negative=False)
    X = sym.IndexedBase('X')
    i = sym.Idx('i', n)
    mX, vX = sym.symbols('mX, vX')  # symbolic mean / variance
    res_mX_Xi = sym.IndexedBase('res_mX_X')[i]  # symbolic mean residual
    qres_mX_Xi = sym.IndexedBase('qres_mX_X')[
        i]  # symbolic squared mean residual
    constraints[res_mX_Xi] = (X[i] - mX)  # mean residual
    constraints[qres_mX_Xi] = (X[i] - mX)**2  # mean residual squared
    # Definition of mean and variance
    constraints[mX] = sym.summation(X[i], i) / n
    constraints[vX] = sym.summation((X[i] - mX)**2, i) / n

    # Second indexed object
    m = sym.symbols('m', integer=True, negative=False)
    Y = sym.IndexedBase('Y')
    j = sym.Idx('j', (0, m - 1))
    mY, vY = sym.symbols('mY, vY')
    vY = sym.symbols('vY')
    res_mY_Yj = sym.IndexedBase('res_mY_Y')[j]
    qres_mY_Yj = sym.IndexedBase('qres_mY_Y')[j]
    constraints[res_mY_Yj] = (Y[j] - mY)
    constraints[qres_mY_Yj] = (Y[j] - mY)**2
    constraints[mY] = sym.summation(Y[j], j) / m
    constraints[vY] = sym.summation((Y[j] - mY)**2, j) / m

    # We now want to find the stats of the combined Z = concat([X, Y])
    k = sym.Idx('k', m + n)
    mZ = sym.symbols('mZ')
    vZ = sym.symbols('vZ')

    # Let t be the total items in Z. This is the sum of the size of its parts.
    t = n + m

    # Define the mean of Z in terms of original sets X and Y
    # We don't know these, but we will use these to verify the general formula
    constraints[mZ, 'terms_X_Y'] = (sym.summation(X[i], i) +
                                    sym.summation(Y[j], j)) / t

    # The expression for mZ in terms of mX and mY is not hard to see
    # The mean of the combined set is easy to compute
    constraints[mZ, 'terms_mX_mY'] = (n * mX + m * mY) / (n + m)

    if 1:
        # Demonstrate equality
        lhs = constraints[mZ, 'terms_X_Y']
        rhs = constraints[mZ, 'terms_mX_mY'].subs(constraints.get_subs(mX, mY))
        assert (rhs - lhs).simplify() == 0

    # Define variance of Z in terms of X and Y.
    # The squared residuals are wrt to mZ this time.
    qres_mZ_Xi = sym.IndexedBase('qres_mZ_X')[i]
    qres_mZ_Yj = sym.IndexedBase('qres_mZ_Y')[j]
    constraints[qres_mZ_Xi, 'raw1'] = (X[i] - mZ)**2
    constraints[qres_mZ_Yj, 'raw1'] = (Y[j] - mZ)**2
    constraints[vZ] = (sym.summation(constraints[qres_mZ_Xi, 'raw1'], i) +
                       sym.summation(constraints[qres_mZ_Yj, 'raw1'], j)) / t

    # Define symbols for the residual terms of Z
    # res_mZ_Zk = sym.IndexedBase('qres_mZ_Z')[k]
    qres_mZ_Zk = sym.IndexedBase('qres_mZ_Z')[k]
    constraints[qres_mZ_Zk] = sym.Piecewise((qres_mZ_Xi.base[k], k < n),
                                            (qres_mZ_Yj.base[k - n], k >= n))
    constraints[qres_mZ_Xi, 'alt1'] = qres_mZ_Zk.subs(k, i)
    constraints[qres_mZ_Yj, 'alt1'] = qres_mZ_Zk.subs(k, n + j)
    constraints[vZ] = (sym.summation(qres_mZ_Zk, k)) / t

    # The variance is tricker to get in terms of mX, mY, vX, and vY.
    # We can note that:
    # (X[i] - mZ) ** 2 =
    # (X[i] - mX + mX - mZ) ** 2 =
    # (This is the step I want to encode symbolically... it can be seen by
    #  considering
    #     ((a + b + c + d) ** 2).expand() =
    #     a**2 + 2*a*b + 2*a*c + 2*a*d + b**2 + 2*b*c + 2*b*d + c**2 + 2*c*d + d**2
    #  but I'd like to make it more clear so the sympy code does not rely on
    #  comments
    # )
    # (X[i] - mX) ** 2 + 2 * (X[i] - mX) * (mX - mZ) + (mX - mZ) ** 2
    if 0:
        # This shows the above step for X
        a, b, c, d = sym.symbols('a, b, c, d')
        let = {a: X[i], b: mX, c: mX, d: mZ}
        expr = ((a - b + c - d)**2).expand()
        term = (a - b)**2
        rest = (expr - term.expand())
        adjust = rest.subs(let).factor()
        adjust = sym.collect(adjust.expand(), [X[i]], func=sym.factor)
        sym.collect(adjust.expand(), [X[i]], func=sym.factor)
        expected_adjust = 2 * (X[i] - mX) * (mX - mZ) + (mX - mZ)**2
        assert expected_adjust.expand().simplify() == adjust.expand().simplify(
        )
        final = adjust + term.subs(let)
        print('final = {!r}'.format(final))

    constraints[qres_mZ_Xi, 'alt2'] = ((X[i] - mX)**2 + 2 * (X[i] - mX) *
                                       (mX - mZ) + (mX - mZ)**2)
    constraints[qres_mZ_Xi, 'alt3'] = (
        # qres_mX_Xi       + 2 * res_mX_Xi   * (mX - mZ) + (mX - mZ) ** 2
        qres_mX_Xi + 2 * (X[i] - mX) * (mX - mZ) + (mX - mZ)**2)

    # And likewise:
    # (Y[j] - mZ) ** 2 =
    # (Y[j] - mY + mY - mZ) ** 2 =
    # (Y[j] - mY) ** 2 + 2 * (Y[j] - mY) * (mY - mZ) + (mY - mZ) ** 2
    constraints[qres_mZ_Yj, 'alt2'] = ((Y[j] - mY)**2 + 2 * (Y[j] - mY) *
                                       (mY - mZ) + (mY - mZ)**2)
    constraints[qres_mZ_Yj, 'alt3'] = (
        qres_mY_Yj + 2 * (Y[j] - mY) * (mY - mZ) + (mY - mZ)**2
        # qres_mY_Yj       + 2 * res_mY_Yj   * (mY - mZ) + (mY - mZ) ** 2
    )

    constraints[
        vZ, 'alt3'] = (sym.summation(constraints[qres_mZ_Xi, 'alt3'], i) +
                       sym.summation(constraints[qres_mZ_Yj, 'alt3'], j)) / t

    def hack_simplify_sum(expr):
        # https://stackoverflow.com/questions/62679575/why-cant-sympy-simplify-these-sum-expressions-of-indexed-variables
        # https://github.com/sympy/sympy/issues/19685

        if expr.func is sym.concrete.summations.Sum:
            body, (idx, lower, upper) = expr.args
            if idx not in body.free_symbols:
                factor = (upper + 1) - lower
                return factor * body
            else:
                if body.func is sym.Mul:
                    factor_out = []
                    factor_in = []
                    for sub_arg in body.args:
                        if idx in sub_arg.free_symbols:
                            factor_in.append(sub_arg)
                        else:
                            factor_out.append(sub_arg)
                    outer = sym.Mul(*factor_out)
                    return outer * sym.summation(sym.Mul(*factor_in),
                                                 (idx, lower, upper))
        return expr

    def distribute(func, expr):
        assert expr.func in [sym.Add, sym.Mul]
        new_args = [func(a) for a in expr.args]
        return expr.func(*new_args)

    # We will use these facts
    constraints[sym.summation(X[i], i)] = mX * n,
    constraints[sym.summation(qres_mX_Xi, i)] = vX * n
    constraints[sym.summation(Y[j], j)] = mY * m
    constraints[sym.summation(qres_mY_Yj, j)] = vY * m

    # Now that we have established the relation between the each squared
    # residual term wrt mZ in terms of mX, we need to establish an expression
    # for the sum of the squared residual terms wrt mZ.
    # The goal is to find a way to cancel any remaining references to X[i]
    qrsum_part1 = sym.summation(constraints[qres_mZ_Xi, 'alt3'], i)
    qrsum_part1 = distribute(hack_simplify_sum, qrsum_part1.expand())
    # We can replace the sum of X with mX * n, because the sum of all items
    # in X will be equal to the mean of X added together n times.
    # similar thing with the sum of the qres
    qrsum_part1 = qrsum_part1.subs({
        sym.summation(X[i], i): mX * n,
        sym.summation(qres_mX_Xi, i): vX * n
    })

    qrsum_part2 = sym.summation(constraints[qres_mZ_Yj, 'alt3'], j)
    qrsum_part2 = distribute(hack_simplify_sum, qrsum_part2.expand())
    qrsum_part2 = qrsum_part2.subs({
        sym.summation(Y[j], j): mY * m,
        sym.summation(qres_mY_Yj, j): vY * m
    })

    constraints[vZ, 'alt4'] = (qrsum_part1 + qrsum_part2) / t

    print(sym.pprint(constraints[vZ, 'alt4']))

    # sym.summation(constraints[qres_mZ_Xi, 'alt3'].subs(constraints.get_subs(res_mX_Xi, qres_mX_Xi)), i).simplify().factor()
    # part1.subs(constraints.get_subs(res_mX_Xi, qres_mX_Xi))

    for a, b in constraints:
        print('\n---\n')
        sym.pprint(a)
        print('=')
        sym.pprint(b)
Exemple #18
0
def __document__():
    n = sp.Symbol('n', integer=True)
    # ある頂点iについて考えるとする
    # よってここのIndexedBaseはlambdifyした後にiとの差を受け取ることになる

    Pi = sp.IndexedBase('P')
    Li = sp.IndexedBase('L')
    Ki = sp.IndexedBase('K')

    # これはiベクトル
    p = sp.IndexedBase('p')

    j, d = [sp.Idx(*spec) for spec in [('j', n - 1), ('d', dim)]]
    j_range, d_range = [(idx, idx.lower, idx.upper) for idx in [j, d]]
    length_j = sp.sqrt(sp.Sum((Pi[j, d] - p[d])**2, d_range)).doit()
    potential_j = Ki[j] * (length_j - Li[j])**2 / 2
    function_E = sp.Sum(potential_j, j_range)

    pE = "{\partial E}"
    pp0 = "{\partial p_0}"
    pp1 = "{\partial p_1}"
    p2E = "{\partial^2 E}"
    pp00 = "{\partial^2 p_0}"
    pp01 = "{\partial p_0 \partial p_1}"
    pp10 = "{\partial p_1 \partial p_0}"
    pp11 = "{\partial^2 p_1}"
    sqrt1 = "\sqrt{"
    sqrt2 = "}"

    markdown(
        r'''
# KK法による無向グラフの描画

無向グラフの描き方として知られているKamada-Kawai法のアルゴリズムを実装する。

全ての頂点がバネによって繋がっているものとしてグラフを描く。バネのエネルギーが
安定している状態のグラフを描いていく。
グラフ全体を一つの系としてみなして、その系のエネルギーが低くなるように、頂点を動かしていく。
まず、ある頂点$v_i$の位置ベクトルを$p$とする。この時、ある頂点$v_j$との距離は、
$${{length}}_j={length_j}$$
で定義される。$v_i$と$v_j$をつなぐバネのポテンシャルはその自然長を$L_j$、バネ係数を$K_j$とすると
$${{potential_j}}={potential_j}$$
と定義できる。よって、$v_i$においてのポテンシャル$E$は、
$${{E}}={function_E}$$
バネの自然長は、頂点$v_i$と$v_j$のグラフ上の距離を$d_{{i,j}}$として
$$L_j=L_0 \times d_{{i,j}}$$と定める。また、バネ係数はそれぞれ
$$K_j=K_0 / d_{{i,j}}^2$$と定める。$K_0$と$L_0$は定数である。
次に、このエネルギーを最小化していき、安定な状態を求める。
そのために、$p=P_i$と定め、$p$だけを変数とみて、考える。
この$p$は各$v_i$の中で
$$\Delta_i = {sqrt1}\left({{\frac{pE}{pp0}}}\right)^2+\left({{\frac{pE}{pp0}}}\right)^2{sqrt2}$$
が最大の$i$とする。そして、$p$の新しい座標を
$$p_0 = p_0 + \delta p_0$$
$$p_1 = p_1 + \delta p_1$$
とする。この$\delta p_0$、$\delta p_1$は次の線形方程式の解である
$$\frac{p2E}{pp00} \delta p_0 + \frac{p2E}{pp01} \delta p_1 = -\frac{pE}{pp0}$$
$$\frac{p2E}{pp10} \delta p_0 + \frac{p2E}{pp11} \delta p_1 = -\frac{pE}{pp1}$$
これを繰り返し、$\Delta_i$の最大値が十分小さくなるまで繰り返す。
以下に、このアルゴリズムを適用する前の図と、適用後の図を示す。



''', **locals())
Exemple #19
0
def kk2(Pos, Lens, Cons, dim=dim, n=n, eps=0.000001):
    start_time = current_time()
    ni = n - 1
    P = sp.IndexedBase('P', shape=(ni, dim))  #ni個のPositions
    L = sp.IndexedBase('L')  #L[j] is Natural Lens between p and P[j]
    K = sp.IndexedBase('K')  #K[j] is p,P[j]間のばね定数
    p = sp.IndexedBase('p')  #Position of one of Pで動かす点
    q = sp.IndexedBase('q')  #最適化の式の解
    #i番目をスライスするよう
    Pos2 = np.concatenate([Pos, Pos],
                          axis=0).astype(np.float64)  #Pos2[i+1:ni+i+1]
    Len2 = np.concatenate([Lens, Lens], axis=0)  #Len2[i+1:ni+i+1,i]
    Cons2 = np.concatenate([Cons, Cons], axis=0)  #Cons2[i+1:ni+i+1,i]

    i, j, d, x = [
        sp.Idx(*spec)
        for spec in [('i', ni), ('j', ni), ('d', dim), ('x', dim)]
    ]
    i_range, j_range = [(idx, idx.lower, idx.upper) for idx in [i, j]]
    d_range = (d, d.lower, d.upper)

    distance_j = sp.sqrt(sp.Sum((p[d] - P[j, d]) * (p[d] - P[j, d]), d_range))
    Energy_j = (K[j] * (distance_j - L[j]) * (distance_j - L[j]) / 2).doit()
    Energy = (sp.Sum(Energy_j, j_range).doit())

    variables = [p[d] for d in range(dim)]
    jac_sp, hess_sp = [
        sp.Matrix([Energy]).jacobian(variables),
        sp.hessian(Energy, variables)
    ]
    jac, hess = [(sp.lambdify((K, L, P, p), f, dummify=False))
                 for f in [jac_sp, hess_sp]]

    #Sum(jac_sp[d]**2,d_range)の代わり
    abs_grad_E_sp = 0
    for i in range(dim):
        abs_grad_E_sp += jac_sp[i] * jac_sp[i]
    abs_grad_E_sp = sp.sqrt(abs_grad_E_sp)

    abs_grad_E = sp.lambdify((K, L, P, p), abs_grad_E_sp, dummify=False)

    #ラムダ式の変数を略記する
    def vars_i(i):
        return Cons2[i + 1:ni + i + 1,
                     i], Len2[i + 1:ni + i + 1,
                              i], Pos2[i + 1:ni + i + 1], Pos2[i]

    max_delta = sp.oo
    max_delta_number = 0
    loop = 0

    while max_delta > eps:
        #方程式を解いて一点を動かす
        ds = np.linalg.solve(hess(*vars_i(max_delta_number)),
                             jac(*vars_i(max_delta_number)).flatten())
        Pos2[max_delta_number] -= ds
        Pos2[max_delta_number + n] -= ds

        #abs_grad_Eの最大となるところを探す
        max_delta = 0
        for i_move in range(n):
            if (abs_grad_E(*vars_i(i_move)) > max_delta):
                max_delta = abs_grad_E(*vars_i(i_move))
                max_delta_number = i_move
        loop += 1
    print('loop=%d' % loop)
    print("かかった時間: {}".format(current_time() - start_time))
    Pos_after = sp.Matrix(Pos2[:n])
    markdown(
        r'''
##kk2(Pos=${Pos}$,Lens=${Lens}$,Cons=${Cons}$)
ループを${loop}$回まわし、最適化した結果、以下が得られました。
$$Pos={Pos_after}$$
''', **locals())

    return Pos2[:n]
Exemple #20
0
# gradient と Hessian
dim = 2

n = sp.Symbol('n', integer=True)
# ある頂点iについて考えるとする
# よってここのIndexedBaseはlambdifyした後にiとの差を受け取ることになる

Pi = sp.IndexedBase('P')
Li = sp.IndexedBase('L')
Ki = sp.IndexedBase('K')

# これはiベクトル
p = sp.IndexedBase('p')

j, d = [sp.Idx(*spec) for spec in [('j', n - 1), ('d', dim)]]
j_range, d_range = [(idx, idx.lower, idx.upper) for idx in [j, d]]
length_j = sp.sqrt(sp.Sum((Pi[j, d] - p[d])**2, d_range)).doit()
potential_j = Ki[j] * (length_j - Li[j])**2 / 2
function_E = sp.Sum(potential_j, j_range)
GRAD_0 = sp.simplify(sp.diff(function_E, p[0]))
GRAD_1 = sp.simplify(sp.diff(function_E, p[1]))
HESS_00 = sp.simplify(sp.diff(GRAD_0, p[0]))
HESS_01 = sp.simplify(sp.diff(GRAD_0, p[1]))
HESS_10 = sp.simplify(sp.diff(GRAD_1, p[0]))
HESS_11 = sp.simplify(sp.diff(GRAD_1, p[1]))
grad_0, grad_1 = [
    sp.lambdify((Pi, Li, Ki, p, n), f, dummify=False)
    for f in [GRAD_0, GRAD_1]
]
hess_00, hess_01, hess_11 = [
Exemple #21
0
def kk2(Pos, Lengthes, Cons, dim=dim, n=n, eps=0.000001):
    P = sp.IndexedBase('P', shape=(dim, n))  #n個のPositions
    L = sp.IndexedBase('L')  #L[i,j] is Natural Lengthes between pi and pj
    K = sp.IndexedBase('K')  #K[i,j] is ij間のばね定数
    p = sp.IndexedBase('p')  #Position of one of Pで動かす点
    q = sp.IndexedBase('q')  #最適化の式の解

    i, j, d, x = [
        sp.Idx(*spec) for spec in [('i', n), ('j', n), ('d', dim), ('x', dim)]
    ]
    i_range, j_range = [(idx, idx.lower, idx.upper - 1) for idx in [i, j]]
    d_range = (d, d.lower, d.upper)

    def exc_i(a):
        return 1 * (a >= i) + a

    distance_j = sp.sqrt(
        sp.Sum((p[d] - P[d, exc_i(j)]) * (p[d] - P[d, exc_i(j)]), d_range))
    Energy_j = (K[i, exc_i(j)] * (distance_j - L[i, exc_i(j)]) *
                (distance_j - L[i, exc_i(j)]) / 2).doit()
    Potential = (sp.Sum(Energy_j, j_range).doit())
    Energy_j_diffx = sp.diff(Energy_j, p[1])

    diff_x = sp.simplify(sp.diff(Potential, p[d]))
    delta_fomula = sp.sqrt(sp.Sum(sp.diff(Potential, p[d])**2, d_range))
    delta_fomula_i_lambda = sp.lambdify((p, i, P, L, K),
                                        delta_fomula,
                                        dummify=False)

    delta_x = sp.Sum(sp.diff(sp.diff(Potential, p[x]), p[d]) * q[d],
                     d_range) + sp.diff(Potential, p[x])
    delta_x_lambda = sp.lambdify((x, q, p, i, P, L, K), delta_x)

    markdown(
        r'''
$$distance_j={distance_j}$$
$$Energy_j={Energy_j}$$
$$Enediff={Energy_j_diffx}$$
$$Potential={Potential}$$
$$diff_x={diff_x}$$
$$delta_fomula={delta_fomula}$$
''', **locals())

    max_delta = 0
    max_delta_number = 0

    #確認用
    def xmi(i, m):
        return Cons[i, m] * (
            (Pos[0, m] - Pos[0, i]) - Lengthes[i, m] *
            (Pos[0, m] - Pos[0, i]) / math.sqrt((Pos[0, m] - Pos[0, i])**2 +
                                                (Pos[1, m] - Pos[1, i])**2))

    def ymi(i, m):
        return Cons[i, m] * (
            (Pos[1, m] - Pos[1, i]) - Lengthes[i, m] *
            (Pos[1, m] - Pos[1, i]) / math.sqrt((Pos[0, m] - Pos[0, i])**2 +
                                                (Pos[1, m] - Pos[1, i])**2))

    def deltam(m):
        tmpx = 0
        tmpy = 0
        for i in range(n):
            if i != m:
                tmpx += xmi(i, m)
                tmpy += ymi(i, m)
        return math.sqrt(tmpx**2 + tmpy**2)

    for i_move in range(n):
        p_move = Pos.T[i_move]
        print(p_move)
        #		print(delta_fomula_i_lambda(p_move,i_move,Pos,Lengthes,Cons))
        print(deltam(i_move))
        #		print(delta_x_lambda(0,q2,p_move,i_move,Pos,Lengthes,Cons))
        #		ds=sp.solve([delta2_x_lambda(x,q,p_move,i_move,Pos,Lengthes,Cons) for x in range(dim)],[q[x] for x in range(dim)])
        for d in range(dim):
            #			Pos[d,i_move] -=ds[q[d]]
            d == d

    return Pos