用两个实值参数替换泛型函数对象的复数值参数

时间:2016-05-28 10:45:34

标签: python generics

scipy中的曲线拟合工具倾向于假设模型函数的参数是实值的。 当将依赖于复值参数的模型拟合到复值数据集时,首先必须创建模型的版本,其中每个复杂参数被两个真实参数替换。

首先,一个简单的例子:

var  selectedIndexPath :NSIndexPath?{
    didSet{
        if let indexpath = selectedIndexPath {
            collectionView.collectionViewLayout.invalidateLayout()
            collectionView.scrollToItemAtIndexPath(indexpath, atScrollPosition:  .CenteredHorizontally, animated: true)
        }
    }
}    

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{
    if indexPath == selectedIndexPath {
        return CGSizeMake(100, 50) // selected size
    }
    else{
        return CGSizeMake(50, 50)// default size
    }
}

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    selectedIndexPath = indexPath
}

 // make sure the first and last cell is at the center of CollectionView most
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets{
    let  firstItemsize = self.collectionView(collectionView, layout: collectionViewLayout, sizeForItemAtIndexPath: NSIndexPath(forItem: 0, inSection: section))

    let numberOfItemsInSection = self.collectionView(collectionView, numberOfItemsInSection: section)
    let lastItemSize = self.collectionView(collectionView, layout: collectionViewLayout, sizeForItemAtIndexPath: NSIndexPath(forItem: numberOfItemsInSection-1, inSection: section))


    return UIEdgeInsetsMake(0, collectionView.frame.size.width/2-firstItemsize.width/2, 0, collectionView.frame.size.width/2-lastItemSize.width/2)
}

//MARK : - UIScrollViewDelegate
func scrollViewDidScroll(scrollView: UIScrollView){
    selectedIndexPath = nil;
}

func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool){
    if !decelerate {
        var centerPoint = collectionView.contentOffset
        centerPoint.x += collectionView.frame.width/2
        centerPoint.y += collectionView.frame.height/2
        selectedIndexPath = collectionView.indexPathForItemAtPoint(centerPoint)
    }
}

func scrollViewDidEndDecelerating(scrollView: UIScrollView){
    var centerPoint = collectionView.contentOffset
    centerPoint.x += collectionView.frame.width/2
    centerPoint.y += collectionView.frame.height/2
    selectedIndexPath = collectionView.indexPathForItemAtPoint(centerPoint)
}   

注意:模型的输出仍然是复值的,但通过适当地定义残差函数可以很容易地解决这个问题。

现在,我不想为每个函数# original function, representing a model where a,b may be complex-valued def f(x, a, b): return a+b*x # modified function, complex parameters have been replaced by two real ones def f_r(x, a_r, a_i, b_r, b_i): return f(x, a_r + 1J*a_i, b_r+1J*b_i) print( f(1,2+3J,4+5J) == f_r(1,2,3,4,5) ) 编写新代码,而是希望有一个"函数工厂"我将函数对象f与一个布尔列表f一起传递给它,指定is_complex的哪些参数被假定为复值(因此需要用两个实数替换)有价值的论据)。 这个布尔值列表可以是例如从与f一起提供的初始值推断出来。

我是这类问题的新手,所以我在网上浏览了decorator module。在进入通用案例​​之前,以下是使用f类的上述示例:

Functionmaker

对于通用情况,现在可以想象合成传递给函数制作者的两个字符串:

import decorator

def f(x, a, b):
    return a+b*x

f_r = decorator.FunctionMaker.create(
    'f_r(x, a_r, a_i, b_r, b_i)',
    'return f(x, a_r + 1J*a_i, b_r + 1J*b_i)',
    dict(f=f))

这似乎解决了这个问题。

我的问题是:这是一种合理的做法吗?在python中有更好/更简单的方法吗?

P.S。我不是计算机科学家,所以如果我使用的技术术语不正确,请随时修改。

1 个答案:

答案 0 :(得分:0)

您不必执行任何讨厌的基于字符串的生成,您只需使用基本函数闭包来创建包装器:

def complex_unroll(f, are_complex):

    # This function will have access to are_complex and f through python closure
    # *args give us access to all parameters as a list
    def g(*args, **kwargs):
        # new_args stores new list of parameters, the complex ones
        new_args = []
        # arg_id is iterator used to keep track where are we in the original list
        arg_id = 0
        for is_complex in are_complex:        
            if is_complex:
                # if we request complex unroll, we merge two consequtive params
                new_args.append(args[arg_id] + 1J*args[arg_id+1])
                # and move iterator 2 slots
                arg_id += 2
            else:
                # otherwise, just copy the argument
                new_args.append(args[arg_id])
                arg_id += 1
        # finally we return a call to original function f with new args
        return f(*new_args, **kwargs)

    # our unroll function returns a newly designed function g
    return g

现在

def f(x, a, b):
    return a+b*x

def f_r(x, a_r, a_i, b_r, b_i):
    return f(x, a_r + 1J*a_i, b_r+1J*b_i)

f_u = complex_unroll(f, [False, True, True])

print f(1,2+3J,4+5J)
print f_r(1,2,3,4,5)
print f_u(1,2,3,4,5)

f_u2 = complex_unroll(f, [True, True, True])

print f_u2(1,0,2,3,4,5)

按需运作。

为什么我更喜欢这条路径而不是问题中提议的路径?

  • 它不使用任何附加模块/库,只是python处理参数和闭包的一个非常基本的机制。特别是你的解决方案做了反思,它分析了定义的函数,与你试图获得的函数相比,这是一个非常复杂的操作。
  • 它处理命名参数就好了,所以如果你有f(x, a, b, flag),你仍然可以使用g = complex_unroll(f, [False, True, True])并调用g(0, 0, 0, 0, 0, flag = True),这会在你的代码中失败。不过,您可以添加对此的支持。