注水算法解功率分配问题-Python

问题描述:

分配总功率PN个信道,使总接收通信速率最大。

问题建模为:
\begin{aligned} &\mathop{\mathrm{min}}\limits_{\mathbf{p} }~ -\sum_{i}^N Wlog_2(1+\frac{h_i p_i}{\sigma^2}) \\ &\mathrm{s.t.}~~ \sum_{i}^Np_i=P \\ &~~~~~~~~p_i\geq 0 \end{aligned}
其中\sigma^2为接收机的噪声功率,W为信道带宽,P为发射机的总功率,p_i为分给第i个接收机、信道增益为h_i的发射功率。

对等式约束引入一个乘子v \in \mathbb{R},对不等式约束引入乘子\mathbf{\lambda}\in \mathbb{R}^N,得到Lagrange函数L:
\begin{aligned} L(\mathbf{p},\mathbf{\lambda},v)=-\sum_{i}^N Wlog_2(1+\frac{h_i p_i}{\sigma^2})-\sum_{i}^N\lambda p_i+v(\sum_{i}^Np_i-P) \\ \end{aligned}

\mathbf{p}^*(\mathbf{\lambda}^*, v^*)分别为原问题和对偶问题的某对最优解,则得到如下KKT条件:

\begin{aligned} &\sum_{i}^Np_i^*=P\\ &p_i^*\geq 0,~i=1,...,N\\ &\lambda_i^*\geq 0,~i=1,...,N\\ &\lambda_i^* p_i^*=0,~i=1,...,N\\ &-\frac{W\log_2e}{\sigma^2/h_i+p_i^*}-\lambda_i^*+v^*=0~i=1,...,N\\ \end{aligned}

最优解之间满足:
p_{i}^*= \begin{cases} \frac{W\log_2 e}{v^*}-\frac{\sigma^2}{h_i} &v^*<\frac{W\log_2 e}{\sigma^2/h_i}\\ 0&v^*\geq \frac{W\log_2 e}{\sigma^2/h_i} \end{cases}

或者,更简洁地,p_i^*=\max \{0,\frac{W\log_2e}{v^*}-\frac{\sigma^2}{h_i}\}。将p_i^*带入条件\sum_{i}^Np_i^*=P得到:
\sum_i^N \max \{0,\frac{W\log_2e}{v^*}-\frac{\sigma^2}{h_i}\}=P
方程左端是\frac{W\log_2e}{v^*}的分段线性增函数,分割点为\sigma^2/h_i,因此上述方程有唯一确定的解。

上述解决问题的方法称为注水。这是因为,可以将\sigma^2/h_i看做第i片区域的水平线,然后对整个区域注水,使其具有深度\frac{W\log_2e}{v^*},所需的总水量为\sum_i^N \max \{0,\frac{W\log_2e}{v^*}-\frac{\sigma^2}{h_i}\},不断注水,直至总水量为P。第i个区域的水位深度即为最优p_i^*

waterfilling.png

上述解需要确定水位\frac{\log_2 e}{v^*},编程上的思想为:

  • 以最差信道的高度\frac{\log_2 e}{\sigma^2/h_{worst}}作为水位,注水(分配功率)
  • 若注水到该水位时功率有剩余,则均分剩余功率;
  • 若水量不足,则移除该最差信道(即不分配功率),重复以上操作。

Python代码:

# -*- coding: utf-8 -*-
"""
2021.01.29 注水法进行功率分配
注水法代码参考 https://pyphysim.readthedocs.io/en/latest/_modules/pyphysim/comm/waterfilling.html
"""

import numpy as np

def waterfilling(Channels, TotalPower, NoisePower):
    """ 注水算法进行功率分配
        Channels: 信道增益
        TotalPower: 待分配的总发射功率
        NoisePower: 接收端的噪声功率
    
    Returns:
        Powers: optimum powers (分配的功率)
        mu: water level (水位)
    """
    ### 降序排列信道增益
    Channels_SortIndexes = np.argsort(Channels)[::-1]
    Channels_Sorted = Channels[Channels_SortIndexes]
    """
    计算接触最差信道的水位,对这个最差的信道分配零功率。
    此后,按此水位为每个信道分配功率,
        如果功率之和少于总功率,则均分剩余功率给各个信道(增加水位);
        如果功率之和多于总功率,则移除最坏信道,重复操作
    """
    N_Channels = Channels.size ## 总信道数
    N_RemovedChannels = 0  ## 移除的信道数
    ## 按最差信道计算最低水位
    WaterLevel = NoisePower / (Channels_Sorted[N_Channels-N_RemovedChannels-1])
    Powers = WaterLevel - (NoisePower /Channels_Sorted[np.arange(0, N_Channels - N_RemovedChannels)])
    
    ## 当功率之和多于总功率时,移除最坏信道,直至总功率够分
    while (sum(Powers)>TotalPower) and (N_RemovedChannels<N_Channels):
        N_RemovedChannels += 1
        WaterLevel = NoisePower / (Channels_Sorted[N_Channels-N_RemovedChannels-1])
        Powers = WaterLevel - (NoisePower /Channels_Sorted[np.arange(0, N_Channels - N_RemovedChannels)])
    
    ## 将剩余的功率均分给各个(剩余的)信道
    Power_Remine = TotalPower-np.sum(Powers)
    Powers_Opt_Temp = Powers + Power_Remine/(N_Channels - N_RemovedChannels)
    
    ## 将功率分配情况按原信道顺序排列
    Powers_Opt = np.zeros([N_Channels])
    Powers_Opt[Channels_SortIndexes[np.arange(0, N_Channels-N_RemovedChannels)]] = Powers_Opt_Temp
    
    WaterLevel = Powers_Opt_Temp[0] + NoisePower / Channels_Sorted[0]
    
    return Powers_Opt, WaterLevel

if __name__ == '__main__':
    """  测试代码
    """
    power_Noise = 1e-8
    channels = np.random.random(10)*1e-10
    alpha = power_Noise/np.array(channels)
    power_Tx = 1000
    powers,waterlevel = waterfilling(np.array(channels), power_Tx, power_Noise)
    print(powers,waterlevel)

    import matplotlib
    import matplotlib.pylab as plt
#    %matplotlib inline
    
    matplotlib.rcParams.update({'font.size': 14})
    buckets = channels.size
    axis = np.arange(0.5,buckets+1.5,1)
    index = axis+0.5
    X = powers.copy()
    Y = alpha+ X
    
    # to include the last data point as a step, we need to repeat it
    A = np.concatenate((alpha,[alpha[-1]]))
    X = np.concatenate((X,[X[-1]]))
    Y = np.concatenate((Y,[Y[-1]]))
    
    plt.xticks(index)
    plt.xlim(0.5,buckets+0.5)
#    plt.ylim(0,1.5)
    plt.step(axis,A,where='post',label =r'$\sigma^2/h_i$',lw=2)
    plt.step(axis,Y,where='post',label=r'$\sigma^2/h_i + p_i$',lw=2)
    plt.legend(loc='upper left')
    plt.xlabel('Bucket Number')
    plt.ylabel('Power Level')
    plt.title('Water Filling Solution')
    plt.show()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容