光子学智能变量优化工具(PIVOT)操作手册

1 PIVOT 简介

随着科学探索越来越深入,各行各业设计都呈现出设计尺度越来越小、设计规模越来越大、集成化程度越来越高的趋势,随之而来的是设计参数空间的维度越来越大,传统的扫描工具在面对日渐增长的设计参数空间维度时,很难在短时间内得到可行解。

优化算法可以在更大设计参数空间上更快速获取可行解,是理想的解决方案。然而,算法开发成本高,虽然也有很多开源算法,然而开源算法一般是通用型算法,必须懂得算法原理后重构部分代码以适配项目,因此要求执行仿真设计优化的员工必须既懂得相关领域的知识、又懂得算法以及编程相关的知识才可以执行设计参数空间很大的仿真设计。

为了解决上述难题,让各行业优秀的设计人才可以专注于设计本身,而不需要花费额外精力学习探索算法,把优化算法只作为类似于仿真的基础工具,逍遥科技推出光子学智能变量优化工具(Photonic Intelligent Variable Optimization Tool , 以下简称 PIVOT)。PIVOT 旨在为用户提供从扫描到优化的进阶,用户只需要 4 行代码即可实现工具的可视化调用,可视化操作界面只需要简单的选择、填写即可调用支持多种并行方案的优化算法。同时,PIVOT 提供灵活的 API 调用,可以为各领域的仿真环境提供服务。PIVOT 也关注用户痛点问题,支持优化中断恢复,方便用户在优化过程中意外中断时提供继续优化的功能,减少了因各种情况增加的时间成本问题。此外,PIVOT 支持实时优化过程记录的功能,实时记录不仅仅支持设计参数和优化目标,还支持记录仿真环境中的其他变量,方便用户为后续分析处理阶段采集相关数据。

现阶段,PIVOT 内置非梯度类优化算法,同时优化目标函数采用最小化并且需要用户在工具外进行定义。

2 PIVOT 功能描述

2.1 PIVOT 设计参数配置

  • 当前脚本:(必填)调用方式下,自动获取调用工具的脚本;

  • 目标函数:(必填)脚本中定义目标函数直接选择;

  • 参数维度:(必填);

  • 参数下边界:(可选);

  • 参数上边界:(可选);

  • 参数精度:(必填)默认为 1e-7;

  • 等式约束:(可选);

  • 不等式约束:(可选);

2.2 PIVOT 算法参数配置

  • 种群大小:(必填)默认为 50;

  • 变异率:(必填)默认为 0.001;

  • 并行数:(可选)默认为矢量化加速,支持填写数字切换至 joblib 并行方案;

  • 迭代次数:(必填)默认为 100;

  • 分类算子:(必填)多种可选;

  • 选择算子:(必填)多种可选;

    • 竞标赛算法留存数:(必填)默认为 3;
  • 交叉算子:(必填)多种可选;

  • 变异算子:(必填)多种可选;

2.3 PIVOT 选项配置

  • 恢复设置;

    • 如果选中恢复,需要选择恢复 json 文件;
  • 记录设置;

    • 记录恢复文件,可选是否配置;

    • 记录恢复步长,默认为 10;

    • 记录参数,其他参数与优化参数一起记录;

    • 记录文件,可选是否配置;

3 案例展示

3.1 可编程光子数字链路的自动化配置

3.1.1 案例简述

可编程光子数字链路由多个可编程单元构成,每个单元通过电控可工作于三种状态,分别是直通状态、交叉状态以及部分耦合状态。其中,直通状态,即上输入端口输入光,从相应上输出端口输出光。交叉状态,也是全耦合状态,即上端口输入光,从下输出端口输出光。部分耦合状态介于两者之前,输出端口输出光的比例随着耦合效率的变化而变化。

本案例由于不引入光的强度阶梯化后的信息用作数字量,因此只考虑直通和交叉两种工作状态。通过控制单元的两种工作状态,从而控制光的输出端口。通过上述描述的配置,光从左下端口输入,每次最多从右侧一个输出端口输出光。

3.1.2 链路搭建

本案例,我们选择强大的光电一体化仿真工具 pSim Plus 搭建链路。为简化搭建链路,我们选择定向耦合器作为单元器件,并且只搭建 2*2 网络。pSim Plus 中定向耦合器通过调节耦合效率可以实现与上述单元一致的三种状态,本案例中只考虑耦合效率为 0 或者为 1,分别对应直通和交叉两种状态。 单元。其中,光从光网络分析仪输出至单元 C_3 左下端口,光分别从 C_11、C_12、C_7、C_5 输出至光网络分析仪

3.1.3 链路参数化

得益于 pSim Plus 强大的脚本支持工具,我们可以在脚本环境在参数化以及优化搭建好的链路模型。下述代码将上一小节创建的链路进行参数化,其中,p 为设计变量一共 12 维。

1
2
3
4
5
6
7
8
9
10
11
12
13
ckt = get_circuit()
ckt.C_1.coupling_coefficients_table_te = [(1.55,p[0])]
ckt.C_2.coupling_coefficients_table_te = [(1.55,p[1])]
ckt.C_3.coupling_coefficients_table_te = [(1.55,p[2])]
ckt.C_4.coupling_coefficients_table_te = [(1.55,p[3])]
ckt.C_5.coupling_coefficients_table_te = [(1.55,p[4])]
ckt.C_6.coupling_coefficients_table_te = [(1.55,p[5])]
ckt.C_7.coupling_coefficients_table_te = [(1.55,p[6])]
ckt.C_8.coupling_coefficients_table_te = [(1.55,p[7])]
ckt.C_9.coupling_coefficients_table_te = [(1.55,p[8])]
ckt.C_10.coupling_coefficients_table_te = [(1.55,p[9])]
ckt.C_11.coupling_coefficients_table_te = [(1.55,p[10])]
ckt.C_12.coupling_coefficients_table_te = [(1.55,p[11])]

3.1.4 构建目标函数

目前,构建目标函数还需要在工具外实现,这里我们在 pSim Plus 的脚本环境中构建目标函数。目标函数输入为设计参数,然后设计参数传递至链路模型,链路模型运行仿真后返回数据,用户需要根据返回的数据构建最终的优化结果。将上述的参数化过程进一步封装成函数,并编写优化目标函数。同时,为后续可以直接生成最优解参数链路,这里链路参数化过程引入了 method 方法, 为 0 调用仿真,用于目标函数,为 1 时导出原理图,方便后续用户可视化操作。

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
def get_obj(p, method = 0):
ckt = get_circuit()
ckt.C_1.coupling_coefficients_table_te = [(1.55,p[0])]
ckt.C_2.coupling_coefficients_table_te = [(1.55,p[1])]
ckt.C_3.coupling_coefficients_table_te = [(1.55,p[2])]
ckt.C_4.coupling_coefficients_table_te = [(1.55,p[3])]
ckt.C_5.coupling_coefficients_table_te = [(1.55,p[4])]
ckt.C_6.coupling_coefficients_table_te = [(1.55,p[5])]
ckt.C_7.coupling_coefficients_table_te = [(1.55,p[6])]
ckt.C_8.coupling_coefficients_table_te = [(1.55,p[7])]
ckt.C_9.coupling_coefficients_table_te = [(1.55,p[8])]
ckt.C_10.coupling_coefficients_table_te = [(1.55,p[9])]
ckt.C_11.coupling_coefficients_table_te = [(1.55,p[10])]
ckt.C_12.coupling_coefficients_table_te = [(1.55,p[11])]
env = get_environment(
wl_start = 1.55,
wl_end = 1.55,
points_num = 1,
freqdomain = 1,
source_power = 0
)
if method == 0:
ckt_inst = ckt.run(env)
data = ckt_inst.get_result("ONA_1")
return data
elif method == 1:
ckt.export_netlist(env)

#### 构造最小化目标函数
## 传入设计参数
## 设计参数传递至链路并运行仿真
## 处理仿真后结果,这个结果越小越好
def obj_func(p):
new_p = np.where(p==0, 0.00001, 0.99999)
data = get_obj(new_p)
obj = np.sqrt((data["input_{}".format(user_defne)]["te_real"])**2+(data["input_{}".format(user_defne)]["te_imag"])**2)
print(obj)
obj_min = np.abs(1-obj)
return obj_min

# p = np.random.randint(0, 2, 12)
user_defne = np.random.randint(0, 6)

3.1.5 调用优化

极简操作,四行代码直达优化。

1
2
3
4
from PIVOT import PIVOT
win = PIVOT()
win.opti.config(globals(), locals())
best_x,best_y = start.opti()

为了直接可视化展示最优解的链路,这里还需要对结果进行下后处理,通过后处理,用户可直观查看优化是否满足需求。

1
2
print(user_defne)
get_obj(best_x, method=1)

3.1.6 优化配置

这里只需要设计参数配置,用户选择在脚本中定义的目标函数。设计参数维度为 12,对应 12 个单元的耦合系数。设计参数上下边界以及精度分别为 0 、1、1,即设计参数只能在 0 和 1 两个量之间变化。

算法配置页以及可选功能配置页根据需求配置即可,该案例默认配置即可。

3.1.7 总结

该案例提供可复现的项目,包含可视化链路以及优化代码,获取代码联系技术支持。

该案例配套 PhotoCAD 版图设计代码,详见 Programmable Photonic Integrated Circuit — PhotoCAD 1.7 documentation (photocad-docs.readthedocs.io)

参考文献

[1] Zhao Z, Chen T S Y, Han Y, et al. Automatic configuration of programmable photonic digital circuits based on genetic algorithms[C]//International Conference on Optoelectronic Materials and Devices (ICOMD 2022). SPIE, 2023, 12600: 250-255.

3.2 基于微环的波分复用链路参数自动设计

3.2.1 案例简述

案例中,集成四个等效微环模型,通过调整微环的等效半径,从而调节光在微环内一周的相变,使四个级联微环的 DROP 端分别在不同波长下输出。

用户输入指定的波长 \(\lambda1\) 、\(\lambda2\) 、\(\lambda3\) 、\(\lambda4\),根据后续设定的优化目标函数,设计会朝着用户理想曲线收敛。

微环等效模型使用定向耦合器模型创建,单个微环模型如下图所示。通过调整等效微环中波导的长度,从而实现微环光程的调整。一般而言,等效模型中波导长度分别选择半周长,从而实现标准微环的仿真。这里,为了使设计参数丰富一点,将两个波导设计为不同长度。

3.2.2 链路优化

为了展示 pSim Plus 强大的脚本能力,这里使用脚本创建微环级联等效链路。

首先,增加器件到链路,使用脚本创建链路时该部分必不可少。

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
def add_components_to_circuit(ckt):
##实例化器件
ckt.ONA_1 = ps.Optical_Network_Analyzer(n_inputs = 4)
ckt.DC_1 = ps.DirectionalCoupler(coupling_te = 1e-1)
ckt.DC_2 = ps.DirectionalCoupler(coupling_te = 1e-1)
ckt.WGD_1 = ps.Waveguide(length=30, ng_te = 4, loss_te =1.5)
ckt.WGD_2 = ps.Waveguide(length=30, ng_te = 4, loss_te =1.5)
ckt.DC_3 = ps.DirectionalCoupler(coupling_te = 1e-1)
ckt.DC_4 = ps.DirectionalCoupler(coupling_te = 1e-1)
ckt.WGD_3 = ps.Waveguide(length=30, ng_te = 4, loss_te =1.5)
ckt.WGD_4 = ps.Waveguide(length=30, ng_te = 4, loss_te =1.5)
ckt.DC_5 = ps.DirectionalCoupler(coupling_te = 1e-1)
ckt.DC_6 = ps.DirectionalCoupler(coupling_te = 1e-1)
ckt.WGD_5 = ps.Waveguide(length=30, ng_te = 4, loss_te =1.5)
ckt.WGD_6 = ps.Waveguide(length=30, ng_te = 4, loss_te =1.5)
ckt.DC_7 = ps.DirectionalCoupler(coupling_te = 1e-1)
ckt.DC_8 = ps.DirectionalCoupler(coupling_te = 1e-1)
ckt.WGD_7 = ps.Waveguide(length=30, ng_te = 4, loss_te =1.5)
ckt.WGD_8 = ps.Waveguide(length=30, ng_te = 4, loss_te =1.5)
##器件参数设置
ckt.ONA_1.x, ckt.ONA_1.y = -600,0
ckt.DC_1.x, ckt.DC_1.y = -200,200
ckt.DC_2.x, ckt.DC_2.y = -200,-200
ckt.WGD_1.x, ckt.WGD_1.y = -400,0
ckt.WGD_2.x, ckt.WGD_2.y = 0,0
ckt.DC_3.x, ckt.DC_3.y = 400,200
ckt.DC_4.x, ckt.DC_4.y = 400,-200
ckt.WGD_3.x, ckt.WGD_3.y = 200,0
ckt.WGD_4.x, ckt.WGD_4.y = 600,0
ckt.DC_5.x, ckt.DC_5.y = 1000,200
ckt.DC_6.x, ckt.DC_6.y = 1000,-200
ckt.WGD_5.x, ckt.WGD_5.y = 800,0
ckt.WGD_6.x, ckt.WGD_6.y = 1200,0
ckt.DC_7.x, ckt.DC_7.y = 1400,200
ckt.DC_8.x, ckt.DC_8.y = 1400,-200
ckt.WGD_7.x, ckt.WGD_7.y = 1200,0
ckt.WGD_8.x, ckt.WGD_8.y = 1600,0

其次,器件之前的链接关系定义也是必须的。

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
#定义器件之间的端口连接以及输入输出
def link_components_in_circuit(ckt):
ckt.link("ONA_1:output", "op_3:DC_1")
ckt.link("DC_1:op_1", "op_1:WGD_2")
ckt.link("WGD_2:op_0", "op_2:DC_2")
ckt.link("DC_2:op_3", "op_1:WGD_1")
ckt.link("WGD_1:op_0", "op_0:DC_1")
ckt.link("DC_2:op_0", "input_0:ONA_1")

ckt.link("DC_1:op_2", "op_3:DC_3")
ckt.link("DC_3:op_1", "op_1:WGD_4")
ckt.link("WGD_4:op_0", "op_2:DC_4")
ckt.link("DC_4:op_3", "op_1:WGD_3")
ckt.link("WGD_3:op_0", "op_0:DC_3")
ckt.link("DC_4:op_0", "input_1:ONA_1")

ckt.link("DC_3:op_2", "op_3:DC_5")
ckt.link("DC_5:op_1", "op_1:WGD_6")
ckt.link("WGD_6:op_0", "op_2:DC_6")
ckt.link("DC_6:op_3", "op_1:WGD_5")
ckt.link("WGD_5:op_0", "op_0:DC_5")
ckt.link("DC_6:op_0", "input_2:ONA_1")

ckt.link("DC_5:op_2", "op_3:DC_7")
ckt.link("DC_7:op_1", "op_1:WGD_8")
ckt.link("WGD_8:op_0", "op_2:DC_8")
ckt.link("DC_8:op_3", "op_1:WGD_7")
ckt.link("WGD_7:op_0", "op_0:DC_7")
ckt.link("DC_8:op_0", "input_3:ONA_1")

创建的链路需要参数化,并且可以返回求解目标函数需要的仿真分析结果。

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
def circuit_operate(method, p):
circuit = get_circuit()
circuit.WGD_1.length = p[0]
circuit.WGD_2.length = p[1]
circuit.WGD_3.length = p[2]
circuit.WGD_4.length = p[3]
circuit.WGD_5.length = p[4]
circuit.WGD_6.length = p[5]
circuit.WGD_7.length = p[6]
circuit.WGD_8.length = p[7]

env = get_environment(
wl_start=1.54,
wl_end=1.55,
points_num=1001,
freqdomain=True,
source_power=0
)
if method == "run" or method == 0:
## 链路仿真
circuit_inst = circuit.run(env)
data = circuit_inst.gain("ONA_1")
return data
elif method == "create" or method == 1:
## 链路生成
circuit.export_netlist(env)

上面函数定义了方法函数,即可实现仿真运行,又可以最后用于最优参数链路的生成。

最后定义目标函数,目标函数为越小越好,通过修改 1542、1544、1546、1548 可以修改波分复用链路的波峰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def get_sim_result(data):
def get_wl_point(wl):
return (int( wl - data["input_0"]["wl"][0]) * (len(data["input_0"]["wl"])-1) / int((data["input_0"]["wl"][-1]-data["input_0"]["wl"][0])) + 1)

goal = 1000 * ((1-np.sqrt(data["input_0"]["te_real"]**2+data["input_0"]["te_imag"]**2)[int(get_wl_point(1542))]) +
(1-np.sqrt(data["input_1"]["te_real"]**2+data["input_1"]["te_imag"]**2)[int(get_wl_point(1544))]) +
(1-np.sqrt(data["input_2"]["te_real"]**2+data["input_2"]["te_imag"]**2)[int(get_wl_point(1546))]) +
(1-np.sqrt(data["input_3"]["te_real"]**2+data["input_3"]["te_imag"]**2)[int(get_wl_point(1548))]))

return (goal)

def goal_func(model_para):
data = circuit_operate(method = "run", p = model_para)
goal = get_sim_result(data)
print(model_para, goal)
return goal

PIVOT 优化工具支持脚本参数输入,例如上述定义的上下边界可以直接输入进系统。

最后,调用 PIVOT 执行优化即可。

1
2
3
4
5
6
7
8
9
10
if __name__ == "__main__":
## 参数初始化
p_lb = [15, 15, 15, 15, 15, 15, 15, 15]
p_ub = [4, 45, 45, 45, 45, 45, 45, 45]

## 导入 AAO 并配置执行优化
from PIVOT import PIVOT
start = PIVOT()
start.opti.config(globals(), locals())
best_x, best_y = start.opti()

为了进一步绘制优化曲线,并生成当前设定下的最优参数链路,可以进一步对结果进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
## 优化后数据处理
# 打印优化结果
print('best_x:', best_x, '\n', 'best_y:', best_y)
# 输出优化链路
circuit_operate(method ="create", p = best_x)
# 绘制优化曲线
import pandas as pd
import matplotlib.pyplot as plt
Y_history = pd.DataFrame(start.all_history_Y)
fig, ax = plt.subplots(2, 1)
ax[0].plot(Y_history.index, Y_history.values, '.', color='red')
Y_history.min(axis=1).cummin().plot(kind='line')
plt.show()

3.2.3 结果展示

👑 Carl Zhao
📋️ 曾经也是追光少年,然而少年归来已不再是少年,但依然在追光的路上。
📧 邮箱:1005513510@qq.com