服务热线:010-62964229
格瑞纳根植于教育行业,让仿真软件从理论教学到应用实践
新闻资讯
当前位置:首页 > 新闻资讯 > 行业新闻
操作教程 | 在AnyLogic中使用Python Part 2:将python转化为模型所用的方法
2023-01-12 16:30:47 发布人:admin 阅读 1861


问题描述

在Python的广泛流行下,使用AnyLogic建模时,可能会遇到下列的情况:

代码级别的熟练建模是对AnyLogic的高级应用,是需要具备一定的编程专业知识的,因此,我们不能指望通过一两篇文章就能学到所有的用法。本文将通过一些简单的示例,为您在AnyLogic中与其他编程语言交互使用提供指引。

方法1:手动将Python代码转换成Java代码

这种方法可以说是比较“简单粗暴”的。但是要能“简单”的用这种方法,你可能需要具备Java、Python以及AnyLogic的专业知识。

特点:无需安装额外的插件,操作简单。

注意:需要一定的专业知识,熟悉Java和Python及AnyLogic。

示例:几个常用方法的转换

很多时候我们用到的Python代码,可能是一段写好的代码段,而我们要使用的也是算法本身而不是必须借助Python,因此,如果可能,我们转换成对应的AnyLogic组件+Java代码会比直接调用Python运行代码要便捷且迅速的多。

我们举例来说:

1.在Python中的导入部分


Import部分的转换可以是与之对应的jar包导入,或者在下方用到此模块功能时自行去写对应的java方法去实现Python此模块函数的功能。

2. 新建并初始化对应的参数

Python代码如下:


转换到AnyLogic中,就是创建参数,设置成对应的类型及初始值






3.Python方法转变

这一部分相对而言是需要更多专业知识的,举例可能参考价值并不大。比如下方,Python的np.zeros方法,到AnyLogic中需要我们改写为相对应的初始化数组。


这时候需要将Python中对应方法所返回的值来写对应的java代码。

将Python方法的转换,通常是改写为AnyLogic的函数方法,便于模型调用,将Python代码块转换成Java代码块作为函数体。


Python方法代码


AnyLogic函数方法

看到这里,想必也很容易发现,这种直接转换的方式更适应于代码量相对并不多的情况,如果我们要使用的是一个代码量非常大,或是要使用Python独有的算法库,亦或是要借助Python训练AI算法,等等,这些情况通过代码转换显然是不现实的。接下来,我们来说另一种使用Python的方法,可以解决上述情景。

方法2:通过Pypeline直接调用Python代码

这种方法需要借助转为Python开发的AnyLogic API —— Pypeline库,通过Pypeline可以实现直接调用本地安装的Python运行相应的Python代码或文件。相较于直接进行代码转换,这种方法是要简单的多,但需要注意,AnyLogic本身是以Java为原生脚本语言的,使用Pypeline可以实现对Python的调用,但要占用额外的计算资源,因此对计算机资源有要求的模型,需要合理使用。

特点:使用方法简单,用法类似于AnyLogic的内置行业库,可以实现与Python交互的目的。

注意:

  1. 需要专门安装Pypeline以及Python。
  2. 需要一定的专业知识,熟悉Java和Python及AnyLogic。

注:关于Pypeline的介绍和安装,可以参考我们的另一篇文章:AnyLogic中使用Python Part 1:Pypeline的安装及使用。

适应场景:

直接使用Python源代码,无需移植到Java;

可以在Python中编写复杂的算法,在Java中直接调用,可选择语言直接传递对象/数据;

能够使用任何 Python 专有库;

使用仿真作为测试平台,测试经过训练的AI策略;

……

示例1:Traveling Salesman模型

我们以AnyLogic自带的示例模型Traveling Salesman为例,说明AnyLogic使用Pypeline实现与Python语言的交互。

模型定义了一个推销员行程优化模型。在这个模型中,存在一组城市信息,销售员以其中一处为始发地城市,也作为行程的归属地。模型仿真了在给定任意数量、位置的城市作为目标城市后,销售员从始发地城市开始出发,途经所有目标城市,最终回到始发地城市的过程。目标是找到整个行程的最短路线。为了优化出行顺序,我们通过Pypeline库使用了Python库OR-tools(由 Google 提供)。


模型与tsp_solver.py交互

  1. Main智能体的启动时:创建python文件里FacilityOrderSolver类的实例solver,将城市间距离的二维列表和始发地城市智能体的下标传给solver。
  2. Order 智能体的buildNewOrder函数:将订单的destinations传给solver的solve函数,获取solve函数计算后的最优送货顺序。

下面我们做一个详细的说明。

外部python文件,tsp_solver.py

class: FacilityOrderSolver

函数:

__init__(self, full_distance_matrix: List[List[float]], home_index: int)

初始化环境,确定归属地、城市节点,已经城市间的距离。

full_distance_matrix:表示所有城市节点之间距离的二维列表。

home_index:作为归属地或起始点的城市节点的下标。

solve(self, indices_to_visit: List[int] = None)

调用优化算法,将初始送货顺序数组优化为最优送货顺序数组。

indices_to_visit:初始送货顺序数组。

返回值:优化后的送货顺序数组。

模型初始化并生成初始订单

注:此部分在Main智能体属性的启动时

生成始发地城市

将模型中所有城市之间的距离数组传入到Python代码块中进行计算。


//获取始发地城市智能体下标

home_index = facilities.findFirst(h -> h.isHomeFacility).getIndex();

// 生成、初始化并导入solver

pyCom.run(

"import random", // 订单智能体内用来生成随机数

"from tsp_solver import FacilityOrderSolver",//从python文件里导入FacilityOrderSolver类

String.format( //参数是城市间距离的二维列表和始发地城市智能体的下标

"solver = FacilityOrderSolver(%s, %s)", //solver是FacilityOrderSolver类的实例

pyCom.toJson(buildDistanceMatrix()),

home_index

)

);

//生成初始订单

if (newOrderOnStartup)

buildNewOrder();

说明:

1.导入的Pypeline插件命名为pyCom

2.通过run方法写入:

"solver = FacilityOrderSolver(%s, %s)",

pyCom.toJson(buildDistanceMatrix()),

home_index

)

导入所需的起点及各位置之间的坐标距离,其中home_index表示模型中的原点,buildDistanceMatrix()表示各位置之间的距离

随机选择numToChoose个城市作为Order智能体的目的地

注:此部分在Order智能体的buildRandOrder函数中

随机选择numToChoose个城市作为Order智能体的目的地,以数组的形式存储目的地城市智能体的下标。

//随机生成目的地个数

int numToChoose = uniform_discr(3, main.facilities.size()-1);

//随机选择numToChoose个城市作为Order智能体的目的地

//code:字符串格式的python语句,用来生成numToChoose个数值,这些数值是0到城市总数之间的整数,并且不包含始发地在内。

String code = String.format("random.sample( set(range(%s)) - set([%s]), %s )", main.facilities.size(), main.home_index, numToChoose,main.home_index);

//toVisit:执行code的返回值,存储该订单对应的所有目的地节点的下标。

int[] toVisit = main.pyCom.runResults(int[].class, code);

return toVisit;

说明:

numToChoose:订单对应的目的地个数

code:字符串格式的python语句,用来生成numToChoose个数值,这些数值是0到城市总数之间的整数,并且不包含始发地在内。

toVisit:执行code的返回值,存储该订单对应的所有目的地节点的下标。

获取从Python中计算后的最优运输路线

注:此部分在Main智能体的buildNewOrder函数中

//生成订单智能体

Order o = add_orders();

//获取订单智能体的目的地城市下标数组

int[] dests = o.destinations;

//获取经算法处理后有序的目的地城市下标数组

int[] newDests = pyCom.runResults(

int[].class,

String.format(

"solver.solve(%s)['order']",

Arrays.toString(dests)

)

);

//将优化后的数组重新赋值给订单智能体的destinations

o.set_destinations(newDests);

//卡车按顺序运输

send(o, truck);

说明:

输入参数:订单的目的地城市的下标数组

算法返回:改变了顺序的订单的目的地城市的下标数组,数组下标的顺序即为运输的先后顺序,即最优运输路径,卡车将依次去往数组存储的下标对应的城市。

示例2:调用Python文件中的一个方法

假设我们在模型中需要使用Python文件Function中的一个函数——velocity方法。

注:

1.下面仅演示方法,无相关的示例模型;

2.假设模型使用的Pypeline插件,使用默认名称,即:pyCommunicator。


要用到的velocity方法

  1. 在模型中的Main智能体>>智能体行动>>启动时,代码区调用function中的velocity方法。

  1. 在需要调用velocity方法的函数中,键入下列代码,获取velocity方法计算后的值:

说明:

pyCommunicator. toJson():通过PyCommunicator插件带的toJson方法,将模型中 x,y表示的二维数组转化为json的形式。

String.format():将python代码转换成字符串形式。

pyCommunicator.run():执行python代码。

pyCommunicator.runResults(): 获取经过Python代码计算后的值。

小结

在Python越来越方便的今天,很多的机器学习等技术以Python为基础进行,再辅助上AnyLogic的可扩展性,让Java与Python在同一平台竞相散发功用,也让建模仿真的应用愈加广泛且适应各类需求。

需要特别说明的是,一是要想真正的在AnyLogic使用Python是需要具备相关的专业知识的。二是,目前版本的AnyLogic都是以Java作为原生脚本语言的,虽然借助Pypeline可以调用Python运行相关文件,但需要额外的计算资源,并且运行速度会受到Python中代码运行的影响,因此,对于规模较大的模型需要注意到这一点。另外,AnyLogic的Python版本在去年已经开发了测试版,或许在不久的将来我们可以直接在AnyLogic中使用Python代码建模,一起期待吧~



  电话:010-62964229
  邮箱:support@carila.cn
  地址:北京市海淀区上地信息产业基地三街3号楼1门4层401
©2021 北京格瑞纳电子产品有限公司 版权所有 京ICP备19024141号 京公网安备 11010802029095号
 
QQ在线咨询
点击这里给我发消息 点击这里给我发消息
服务热线
010-62964229
E-mail
support@
carila.cn
地址:北京市海
淀区上地信息产
业基地三街3号