格式化应用二分法

时间:2014-06-07 14:28:25

标签: python numpy bisection

我需要找到一个广义状态空间的根。也就是说,我有一个维度grid=AxBx(...)xX的离散网格,其中我不知道它有多少维度(解决方案应该适用于任何grid.size)。

我想使用bisection methodf(z) = 0内的每个州z找到根(grid)。说remainder包含f(z),我知道f'(z) < 0。然后我需要

    如果z&gt;
  • 增加remainder 0
  • 如果z&lt;
  • 减少remainder 0

Wlog,假设形状history的矩阵(grid.shape, T)包含网格中每个点的z的早期值的历史记录,我需要增加z(因为remainder&gt; 0)。然后,我需要在zAlternative内选择history[z, :],这是{最小的那些,大于z“。在伪代码中,即:

zAlternative =  hist[z,:][hist[z,:] > z].min()

I had asked this earlier。我给出的解决方案是

b = sort(history[..., :-1], axis=-1)
mask = b > history[..., -1:]
index = argmax(mask, axis=-1)
indices = tuple([arange(j) for j in b.shape[:-1]])
indices = meshgrid(*indices, indexing='ij', sparse=True)
indices.append(index)
indices = tuple(indices)
lowerZ = history[indices]

b = sort(history[..., :-1], axis=-1)
mask = b <= history[..., -1:]
index = argmax(mask, axis=-1)
indices = tuple([arange(j) for j in b.shape[:-1]])
indices = meshgrid(*indices, indexing='ij', sparse=True)
indices.append(index)
indices = tuple(indices)
higherZ = history[indices]

newZ = history[..., -1]
criterion = 0.05
increase = remainder > 0 + criterion
decrease = remainder < 0 - criterion
newZ[increase] = 0.5*(newZ[increase] + higherZ[increase])
newZ[decrease] = 0.5*(newZ[decrease] + lowerZ[decrease])

但是,此代码对我来说不再适用。承认这一点我感到非常糟糕,但我从未理解指数所发生的魔力,因此我很遗憾需要帮助。

代码实际上做了什么,它分别给我最低 最高。也就是说,如果我修复了两个特定的z值:

history[z1] = array([0.3, 0.2, 0.1])
history[z2] = array([0.1, 0.2, 0.3])

我会得到higherZ[z1] = 0.3lowerZ[z2] = 0.1,即极值。两种情况的正确值都是0.2。这里出了什么问题?

如果需要,为了生成测试数据,您可以使用

history = tile(array([0.1, 0.3, 0.2, 0.15, 0.13])[newaxis,newaxis,:], (10, 20, 1))
remainder = -1*ones((10, 20))

测试第二种情况。

预期结果

我调整了上面的history变量,给出了向上和向下的测试用例。预期结果将是

lowerZ = 0.1 * ones((10,20))
higherZ = 0.15 * ones((10,20))

对于历史记录中的每个点z [z,:],下一个最高的前一个值(higherZ)和下一个最小的前一个值(lowerZ)。由于所有积分z都具有完全相同的历史记录([0.1, 0.3, 0.2, 0.15, 0.13]),因此lowerZhigherZ的所有积分都具有相同的值。当然,一般来说,每个z的历史记录会有所不同,因此两个矩阵在每个网格点上都会包含可能不同的值。

2 个答案:

答案 0 :(得分:1)

我将您在此处发布的内容与the solution for your previous post进行了比较,并发现了一些差异。

对于较小的 z,你说

mask = b > history[..., -1:]
index = argmax(mask, axis=-1)
他们说:

mask = b >= a[..., -1:]
index = np.argmax(mask, axis=-1) - 1

对于较大的 z,你说

mask = b <= history[..., -1:]
index = argmax(mask, axis=-1)
他们说:

mask = b > a[..., -1:]
index = np.argmax(mask, axis=-1)

使用the solution for your previous post,我得到:

import numpy as np
history = np.tile(np.array([0.1, 0.3, 0.2, 0.15, 0.13])[np.newaxis,np.newaxis,:], (10, 20, 1))
remainder = -1*np.ones((10, 20))

a = history

# b is a sorted ndarray excluding the most recent observation
# it is sorted along the observation axis
b = np.sort(a[..., :-1], axis=-1)

# mask is a boolean array, comparing the (sorted)
# previous observations to the current observation - [..., -1:]
mask = b > a[..., -1:]

# The next 5 statements build an indexing array.
# True evaluates to one and False evaluates to zero.
# argmax() will return the index of the first True,
# in this case along the last (observations) axis.
# index is an array with the shape of z (2-d for this test data).
# It represents the index of the next greater
# observation for every 'element' of z.
index = np.argmax(mask, axis=-1)

# The next two statements construct arrays of indices
# for every element of z - the first n-1 dimensions of history.
indices = tuple([np.arange(j) for j in b.shape[:-1]])
indices = np.meshgrid(*indices, indexing='ij', sparse=True)

# Adding index to the end of indices (the last dimension of history)
# produces a 'group' of indices that will 'select' a single observation
# for every 'element' of z
indices.append(index)
indices = tuple(indices)
higherZ = b[indices]

mask = b >= a[..., -1:]
# Since b excludes the current observation, we want the
# index just before the next highest observation for lowerZ,
# hence the minus one.
index = np.argmax(mask, axis=-1) - 1
indices = tuple([np.arange(j) for j in b.shape[:-1]])
indices = np.meshgrid(*indices, indexing='ij', sparse=True)
indices.append(index)
indices = tuple(indices)
lowerZ = b[indices]
assert np.all(lowerZ == .1)
assert np.all(higherZ == .15)

似乎有用

答案 1 :(得分:0)

用于history中下一个最高和最低观测值的z形阵列 相对于当前的观察,鉴于目前的观察是history[...,-1:]

通过操纵history的步幅来构造更高和更低的数组 使得更容易迭代z的每个元素的观察结果。 这是使用numpy.lib.stride_tricks.as_strided和n-dim泛化完成的 功能在Efficient Overlapping Windows with Numpy找到 - 我将在最后包含它的来源

有一个python循环,对{10},(10,20,x)的history.shape进行200次迭代。

import numpy as np

history = np.tile(np.array([0.1, 0.3, 0.2, 0.15, 0.13])[np.newaxis,np.newaxis,:], (10, 20, 1))
remainder = -1*np.ones((10, 20))

z_shape = final_shape = history.shape[:-1]
number_of_observations = history.shape[-1]
number_of_elements_in_z = np.product(z_shape)

# manipulate histories to efficiently iterate over
# the observations of each "element" of z
s = sliding_window(history, (1,1,number_of_observations))
# s.shape will be (number_of_elements_in_z, number_of_observations)

# create arrays of the next lower and next higher observation
lowerZ = np.zeros(number_of_elements_in_z)
higherZ = np.zeros(number_of_elements_in_z)
for ndx, observations in enumerate(s):
    current_observation = observations[-1]
    a = np.sort(observations)
    lowerZ[ndx] = a[a < current_observation][-1]
    higherZ[ndx] = a[a > current_observation][0]

assert np.all(lowerZ == .1)
assert np.all(higherZ == .15)
lowerZ = lowerZ.reshape(z_shape)
higherZ = higherZ.reshape(z_shape)
来自Efficient Overlapping Windows with Numpy

sliding_window
import numpy as np
from numpy.lib.stride_tricks import as_strided as ast
from itertools import product

def norm_shape(shape):
    '''
    Normalize numpy array shapes so they're always expressed as a tuple, 
    even for one-dimensional shapes.

    Parameters
        shape - an int, or a tuple of ints

    Returns
        a shape tuple

    from http://www.johnvinyard.com/blog/?p=268
    '''
    try:
        i = int(shape)
        return (i,)
    except TypeError:
        # shape was not a number
        pass

    try:
        t = tuple(shape)
        return t
    except TypeError:
        # shape was not iterable
        pass

    raise TypeError('shape must be an int, or a tuple of ints')


def sliding_window(a,ws,ss = None,flatten = True):
    '''
    Return a sliding window over a in any number of dimensions

    Parameters:
        a  - an n-dimensional numpy array
        ws - an int (a is 1D) or tuple (a is 2D or greater) representing the size 
             of each dimension of the window
        ss - an int (a is 1D) or tuple (a is 2D or greater) representing the 
             amount to slide the window in each dimension. If not specified, it
             defaults to ws.
        flatten - if True, all slices are flattened, otherwise, there is an 
                  extra dimension for each dimension of the input.

    Returns
        an array containing each n-dimensional window from a

    from http://www.johnvinyard.com/blog/?p=268
    '''

    if None is ss:
        # ss was not provided. the windows will not overlap in any direction.
        ss = ws
    ws = norm_shape(ws)
    ss = norm_shape(ss)

    # convert ws, ss, and a.shape to numpy arrays so that we can do math in every 
    # dimension at once.
    ws = np.array(ws)
    ss = np.array(ss)
    shape = np.array(a.shape)


    # ensure that ws, ss, and a.shape all have the same number of dimensions
    ls = [len(shape),len(ws),len(ss)]
    if 1 != len(set(ls)):
        error_string = 'a.shape, ws and ss must all have the same length. They were{}'
        raise ValueError(error_string.format(str(ls)))

    # ensure that ws is smaller than a in every dimension
    if np.any(ws > shape):
        error_string = 'ws cannot be larger than a in any dimension. a.shape was {} and ws was {}'
        raise ValueError(error_string.format(str(a.shape),str(ws)))

    # how many slices will there be in each dimension?
    newshape = norm_shape(((shape - ws) // ss) + 1)
    # the shape of the strided array will be the number of slices in each dimension
    # plus the shape of the window (tuple addition)
    newshape += norm_shape(ws)
    # the strides tuple will be the array's strides multiplied by step size, plus
    # the array's strides (tuple addition)
    newstrides = norm_shape(np.array(a.strides) * ss) + a.strides
    strided = ast(a,shape = newshape,strides = newstrides)
    if not flatten:
        return strided

    # Collapse strided so that it has one more dimension than the window.  I.e.,
    # the new array is a flat list of slices.
    meat = len(ws) if ws.shape else 0
    firstdim = (np.product(newshape[:-meat]),) if ws.shape else ()
    dim = firstdim + (newshape[-meat:])
    # remove any dimensions with size 1
    dim = filter(lambda i : i != 1,dim)
    return strided.reshape(dim)