Python에서 시뮬레이션 모델을위한 에이 젠드 기반 프레임 워크 개발

이전 게시물에서 저는 전장 그리드에 위치 할 수있는 에이전트 그룹을 포함하는 간단한 에이전트 기반 시뮬레이션 모델을 구성했습니다. 모델은 시각화를 위해 matplotlib를 사용하여 Python으로 코딩되었습니다. 계속해서 간단한 시뮬레이션 실행을 수행하여 하나의 전투 시나리오와 그 결과를 보여주었습니다.

아래에서는 코드를 정리하고 기능을 재사용 가능한 기능으로 모듈화하여 문제를 리모델링했습니다. 그 결과 매우 진보 된 에이전트 기반 시뮬레이션 모델을 구축하는 데 사용할 수있는 모듈 식 프레임 워크와 일반 평가 설명이 생성됩니다.

에이전트는 아래와 같이 클래스로 모델링됩니다.

# 클래스, 에이전트를 추상 데이터 유형으로 정의
class agent:
    # 에이전트의 생성자 메소드 인 init-method
    def __init__(self,x,y,group):
        self.life = 100 # 에이전트의 수명 점수
        self.x = x
        self.y = y
        self.group = group

전장 자체는 아래와 같이 2 차원 격자 배열로 모델링됩니다.

# 파이썬에서 목록 이해력을 사용하여 빈 100 x 100 목록 만들기
battlefield = [[None for i in range(0,100)] for i in range(0,100)]

agentCreator 기능을 사용하여 에이전트 그룹을 전장 그리드에 채울 수 있습니다.

# 에이전트를 생성하고 그리드에 할당하는 기능을 정의합니다.
def agentCreator(size,group,groupList,field,n,m):
    # 전체 그룹을 통해 루프, 즉이 경우 1000 개 단위
    for j in range(0,size):
        # 임의의 사용 가능한 위치 선택 
        while True:
            # 임의의 x 좌표
            x = random.choice(range(0,n))
            # 임의의 y 좌표
            y = random.choice(range(0,m))
            # 자리가 있는지 확인하십시오. 그렇지 않다면 다시 반복하십시오
            if field[x][y] == None:
                field[x][y] = agent(x=x,y=y,group=group)
                # 그룹 목록에 에이전트 개체 참조 추가
                groupList.append(field[x][y])
                # while 루프를 종료합니다. 현장에서 자리를 잡다
                break

전장 그리드에 시각적 점으로 생명 점수가 긍정적 인 에이전트를 플로팅하기 위해 시뮬레이션 동안 언제든지 호출하여 현재 전투 상태의 스냅 샷을 얻을 수있는 플로팅 함수를 만듭니다.

# matplotlib에서 pyplot과 색상 가져 오기
from matplotlib import pyplot, colors
# 전장을 그리는 기능을 정의합니다 (아직 살아있는 모든 에이전트).
def plotBattlefield(populationArr, plotTitle):
    # matplotlib의 색상을 사용하여 색상 맵을 정의합니다.
    colormap = colors.ListedColormap(["lightgrey","green","blue"])
    # pyplot을 사용하여 그림 크기 정의
    pyplot.figure(figsize = (12,12))
    # pyplot을 사용하여 제목 추가
    pyplot.title(plotTitle, fontsize = 24)
    # pyplot을 사용하여 x 및 y 레이블 추가
    pyplot.xlabel("x coordinates", fontsize = 20)
    pyplot.ylabel("y coordinates", fontsize = 20)
    # pyplot을 사용하여 x 및 y 축 눈금 조정
    pyplot.xticks(fontsize = 16)
    pyplot.yticks(fontsize = 16)
    # pyplot의 .imshow () 메서드를 사용하여 에이전트 위치 시각화
    pyplot.imshow(X = populationArr, cmap = colormap)

내가 필요로 할 또 다른 기능은 A 유형의 에이전트가 2 차원 전장 그리드의 주어진 셀 내에 여전히 존재하고 살아있는 경우 전장 상태를 1.0 값을 포함하는 숫자 2 차원 배열에 매핑 할 수있는 함수입니다. 유형 B의 에이전트가있는 경우 값 2.0입니다.이 매핑 프로세스를 사용하여 추상, 즉 사용자 정의 된 클래스의 개체가있는 그리드 대신 숫자 그리드를 플로팅 함수에 제공합니다.

#이 함수는 전장 그리드를 A 유형의 에이전트에 대해 1, 유형 B에 대해 2, 에이전트가없는 경우 0으로 숫자 그리드에 매핑합니다.
def mapBattlefield(battlefieldArr):
    # .imshow ()에는 float 요소가있는 행렬이 필요합니다.
    populationArr = [[0.0 for i in range(0,100)] for i in range(0,100)]
    # 에이전트가 A 형이면 1.0을, B 형이면 2.0을 넣으십시오.
    for i in range(1,100):
        for j in range(1,100):
            if battlefieldArr[i][j] == None: # 비어 있음
                pass # 인구 세포에 0.0을 남깁니다.
            elif battlefieldArr[i][j].group == "A": # 그룹 A 상담원
                populationArr[i][j] = 1.0 # 1.0은 "A"를 의미합니다.
            else: # 그룹 B 상담원 # 명
                populationArr[i][j] = 2.0 # 2.0은 "B"를 의미합니다.
    # 매핑 된 값 반환
    return(populationArr)

이러한 모델 구성 요소를 사용하여 초기 전장 인구를 생성하고 matplotlib를 사용하여 에이전트 위치를 표시했습니다. 이것은 아래 코드에서 수행되며 이전 게시물과 유사하지만이 경우에만 모듈화 된 기능을 사용합니다. 초기 전장 그리드 설정 기능은 아래의 별도 기능으로 이전됩니다. 그런 다음 해당 기능이 실행됩니다.

# 초기 전장 그리드 생성 기능
def initBattlefield(populationSizeA,populationSizeB,battlefieldArr):
    # 파이썬에서 목록 이해력을 사용하여 새로운 빈 전장 그리드 초기화
    battlefieldArr = [[None for i in range(0,100)] for i in range(0,100)]
    # 나중에 에이전트 참조를 포함 할 빈 목록을 만들고 A & B를 입력합니다.
    agents_A = []
    agents_B = []
    # 그룹 A와 B의 에이전트에 무작위 스팟 할당; 
    import random
    agentCreator(size = populationSizeA,
                    group = "A",
                    groupList = agents_A,
                    field = battlefieldArr,
                    n = 100,
                    m = 100)
    agentCreator(size = populationSizeB,
                    group = "B",
                    groupList = agents_B,
                    field = battlefieldArr,
                    n = 100,
                    m = 100)
    # 채워진 전장 그리드 반환
    return(battlefieldArr)

# 두 그룹 모두에 대해 인구 1000 명에 대해 위의 기능을 실행
battlefield = initBattlefield(populationSizeA=1000,populationSizeB=1000,battlefieldArr = battlefield)

# 플롯 전장 상태
plotBattlefield(populationArr = mapBattlefield(battlefield), 
                    plotTitle = "battlefield before simulation run (green = A, blue = B)")

이전 게시물에서 다음 규칙에 따라 시뮬레이션 실행을 실행했습니다.

그룹 A는 각 라운드에서 항상 같은 에이전트를 공격하는 전략을 가지고 있습니다. 그룹 B는 적을 공격하기위한 무작위적이고 독립적 인 전략을 가지고 있습니다. 즉, B 유형의 각 에이전트는 해당 에이전트가 도달하는 범위 내에서 무작위로 선택된 에이전트를 공격합니다. 시뮬레이션은 다음 조건에서 수행되었습니다.

1) 각 라운드는 한 번의 반복입니다.

2) 각 라운드에서 각 에이전트는 도달 범위 내에서 한 에이전트를 공격 할 수 있습니다.

3) 에이전트의 도달 범위는 시뮬레이션 시작시 정의되며 기본값은 10입니다.

4) 에이전트가 죽으면 더 이상 전장에 위치하지 않습니다.

5) 에이전트는 자신의 생명 점수가 0 이하일 때 사망합니다.

6) 각 에이전트는 10에서 60까지 무작위로 분산 된 공격 데미지를가집니다.

7) 각 라운드에서 모든 에이전트는 공격을 시작합니다.

이전 게시물 중 하나 에서처럼 이제 50 라운드의 전투를 반복합니다. 각 라운드가 끝나면 생명 점수가 아닌 요원은 전장 그리드에서 제거됩니다. 그러나 이전 게시물과의 차이로 이제 기능을 모듈화 할 것입니다.

먼저 전장 그리드에서 에이전트를 제거하는 기능을 정의합니다.

# 생명 점수가 엄격하게 양수가 아닐 때 전장 그리드에서 에이전트를 제거하는 기능
def removeDeadAgents(battlefieldArr):
    # 점수 이하의 생활 점수를 가진 에이전트 식별-그리드에서 제거
    for i in range(0,len(battlefieldArr)):
        for j in range(0,len(battlefieldArr)):
            if battlefieldArr[i][j]:
                if battlefieldArr[i][j].life <= 0:
                    # 수명 점수가 엄격하게 양수가 아니므로이 에이전트를 제거합니다.
                    battlefieldArr[i][j] = None

다음으로, A 유형의 에이전트에 대한 전투 전략을 구현하는 기능을 정의합니다.

# A 형 에이전트에 대해 한 라운드의 전투를 구현하는 기능
def oneRoundAgentA(i,j,attackRange):
    found_i = None
    found_j = None
    # 각 반복에 대해 동일한 순서로 인접 셀을 찾습니다.
    for k in range(i-attackRange,i+attackRange+1):
        for l in range(j-attackRange,j+attackRange+1):
            # 음수 인덱스 값을 확인합니다. 그렇다면-휴식!
            if k < 0 or l < 0:
                break
                # 99 이상의 인덱스 값을 확인하십시오. 그렇다면 중단하십시오!
            if k > 99 or l > 99:
                break
            if battlefield[k][l]:
                if battlefield[k][l].group == "B": # 그러면 이것은 적입니다
                    if found_i == None:
                        found_i = k
                        found_j = l
                    
    # 식별 된 적에게 피해를줍니다.
    if found_i:
        battlefield[found_i][found_j].life = battlefield[found_i][found_j].life - random.randint(10,60)

그런 다음 B 유형의 에이전트에 대해 동일한 작업을 수행합니다.

# 유형 B의 에이전트에 대해 한 라운드의 전투를 구현하는 기능
def oneRoundAgentB(i,j,attackRange):
    # 먼저 주변 셀 중 하나에 적이 있는지 확인
    enemy_found = False
    for k in range(i-attackRange,i+attackRange+1):
        for l in range(j-attackRange,j+attackRange+1):
            # 음수 색인을 확인합니다. 그렇다면 다음 반복으로 중단됩니다.
            if k < 0 or l < 0:
                break
                # 99보다 큰 인덱스 값을 확인하십시오.
            if k > 99 or l > 99:
                break
            if battlefield[k][l] != None:
                if battlefield[k][l].group == "A":
                    enemy_found = True
    # 임의의 행과 임의의 열을 선택
    found_i = None
    found_j = None
    while enemy_found and found_i == None:
        k = random.randint(i-attackRange,i+attackRange)
        l = random.randint(j-attackRange,j+attackRange)
        # 음수 색인을 확인합니다. 그렇다면 다음 반복을 계속합니다.
        if k < 0 or l < 0:
            continue
        # 색인 값> 99를 확인합니다. 그렇다면 계속합니다.
        if k > 99 or l > 99:
            continue
        if k != i:
            if battlefield[k][l]: 
                if battlefield[k][l].group == "A":
                    found_i = k
                    found_j = l
        else:
            if l != j:
                if battlefield[k][l]:
                    if battlefield[k][l].group == "A":
                        found_i = k
                        found_j = l
    # 식별 된 적에게 피해를줍니다.
    if found_i:
        battlefield[found_i][found_j].life = battlefield[found_i][found_j].life - random.randint(10,60)

이미 구현 된 기능을 사용하여 전투 시뮬레이션을 진행합니다.

for counter in range(0,50): #이 경우 50 회 반복
    # 전장의 모든 셀을 반복
    for x in range(0,len(battlefield)):
        for y in range(0,len(battlefield)):
            # 각 셀 내에 에이전트가 있는지 확인
            if battlefield[x][y] != None:
                # 유형에 따라 : 각각의 공격 전략 실행
                if battlefield[x][y].group == "A":
                    # A 형 요원 1 라운드 전투
                    oneRoundAgentA(i = x, j = y,attackRange=10)
                else: 
                    # B 형 요원을위한 1 라운드 전투
                    oneRoundAgentB(i = x, j = y,attackRange=10)
    # 점수 이하의 생활 점수를 가진 에이전트 식별-그리드에서 제거
    removeDeadAgents(battlefieldArr = battlefield)
# 플롯 전장 상태
plotBattlefield(populationArr = mapBattlefield(battlefield), 
                plotTitle = "battlefield after 50 iterations (green = A, blue = B)")

지금까지 이것은 이전 게시물에서 제시 한 간단한 시뮬레이션 연구와 내용면에서 동일했습니다.

이제 전투 결과와 전투 자체의 진행 상황을 분석하는 그래프를 추가하고 싶습니다. 따라서 유형별로 에이전트 수를 표시하는 추가 플로팅 기능을 정의합니다.

또한 아직 살아있는 에이전트의 시계열을 업데이트하는 기능을 구현합니다.

# 살아있는 에이전트의 시계열을 업데이트하는 함수
def calcAgentsAliveA(resultsA,battlefieldArr):
    #이 변수는 살아있는 에이전트 수를 계산하는 데 사용됩니다.
    countA = 0
    countB = 0
    # 그리드를 반복하고 에이전트를 찾고 관련이있는 경우 개수를 업데이트합니다.
    for i in range(0,len(battlefieldArr)):
        for j in range(0,len(battlefieldArr)):
            if battlefieldArr[i][j]:
                if battlefieldArr[i][j].group == "A":
                    countA = countA + 1
                else:
                    countB = countB + 1
    # 결과 목록을 업데이트하고 반환
    resultsA.append(countA)
    return(resultsA)

# 살아있는 에이전트의 시계열을 업데이트하는 함수
def calcAgentsAliveB(resultsB,battlefieldArr):
    #이 변수는 살아있는 에이전트 수를 계산하는 데 사용됩니다.
    countA = 0
    countB = 0
    # 그리드를 반복하고 에이전트를 찾고 관련이있는 경우 개수를 업데이트합니다.
    for i in range(0,len(battlefieldArr)):
        for j in range(0,len(battlefieldArr)):
            if battlefieldArr[i][j]:
                if battlefieldArr[i][j].group == "A":
                    countA = countA + 1
                else:
                    countB = countB + 1
    # 결과 목록을 업데이트하고 반환
    resultsB.append(countB)
    return(resultsB)

# 아직 살아있는 에이전트 수를 표시하는 함수
def plotNumberOfAgentsAlive(plotTitle,iterations,resultsA,resultsB):
    from matplotlib import pyplot
    pyplot.figure(figsize = (12,12))
    pyplot.title(plotTitle, fontsize = 24)
    pyplot.xlabel("iteration", fontsize = 20)
    pyplot.ylabel("agents still alive", fontsize = 20)
    pyplot.xticks(fontsize = 16)
    pyplot.yticks(fontsize = 16)
    ax = pyplot.subplot()
    ax.plot(iterations,resultsA, label = "type a agents")
    ax.plot(iterations,resultsB, label = "type b agents")
    ax.legend(fontsize=16)

이제 그래프를 표시하여 다른 시뮬레이션 실행을 수행 할 수 있습니다. 다양한 시뮬레이션 시나리오를 실행할 것이므로 시뮬레이션 실행도 함수에 작성합니다.

# 시뮬레이션 실행을위한 기능 정의
def simulationRun(iterationLimit,attackRange,showPlot):
    iterations = []
    resultsA = []
    resultsB = []
    for counter in range(0,iterationLimit): #이 경우 50 회 반복
        iterations.append(counter+1)
        resultsA = calcAgentsAliveA(resultsA,battlefield)
        resultsB = calcAgentsAliveB(resultsB,battlefield)
        # 전장의 모든 셀을 반복
        for x in range(0,len(battlefield)):
            for y in range(0,len(battlefield)):
                # print ( "상위 계층 반복, i :"+ str (i) + ", j :"+ str (j))
                # 각 셀 내에 에이전트가 있는지 확인
                if battlefield[x][y]:
                    # 유형에 따라 : 각각의 공격 전략 실행
                    if battlefield[x][y].group == "A":
                        # A 형 요원 1 라운드 전투
                        oneRoundAgentA(i = x, j = y, attackRange = attackRange)
                    else: 
                        # B 형 요원을위한 1 라운드 전투
                        oneRoundAgentB(i = x, j = y, attackRange = attackRange)
        # 점수 이하의 생활 점수를 가진 에이전트 식별-그리드에서 제거
        removeDeadAgents(battlefieldArr = battlefield)
    # 플롯 전투 진행, 그러나 플롯이 표시되어야하는 경우에만
    if showPlot:
        plotNumberOfAgentsAlive("battle progression",iterations,resultsA,resultsB)
    # 반환 결과 개
    return([resultsA,resultsB])

이제 한 줄의 코드 만 사용하여 시뮬레이션 실행을 수행 할 수 있습니다.

battlefield = initBattlefield(populationSizeA = 1000,populationSizeB = 1000,battlefieldArr = battlefield)
results = simulationRun(iterationLimit = 50, attackRange = 10, showPlot = True)

유형 B의 에이전트는 다소 효과적인 공격 전략을 가지고있는 것 같습니다. 그러나 전투가 시작될 때 그들의 수가 약간 적 으면 어떨까요? 아래에서는 1000 명의 초기 A 에이전트와 950 명의 초기 B 에이전트와의 전투에서 전투 진행 상황을 표시합니다.

battlefield = initBattlefield(populationSizeA = 1000,populationSizeB = 950,battlefieldArr = battlefield)
results = simulationRun(iterationLimit = 50, attackRange = 10, showPlot = True)

공격 범위가 5로 줄어들면 어떻게 되나요?

battlefield = initBattlefield(populationSizeA = 1000,populationSizeB = 1000,battlefieldArr = battlefield)
results = simulationRun(iterationLimit = 50, attackRange = 5, showPlot = True)

에이전트의 초기 전장 위치는 무작위입니다. 따라서 최종 전투 결과도 무작위로 배포 될 수 있습니다. 결과가 어느 정도 무작위 화되는지 조사하고 싶습니다. 이를 위해 시뮬레이션을 반복해서 수행하는 감도 테스트 기능을 구현합니다. 이 함수는 시뮬레이션 종료시 살아있는 총 에이전트 수를 나타내는 히스토그램으로 시각화 할 수있는 결과를 반환합니다. 이 경우 시뮬레이션을 50 번 반복합니다.

아래에서 감도 테스트를 수행하는 기능을 구현합니다.

#이 기능은 전투 결과에 대한 민감도 테스트를 수행하는 데 사용됩니다.
def sensitivityTest(iterationLimit,attackRange,runs):
    # 전장은 전역 변수임을 나타냅니다.
    global battlefield
    # 전투 종료시 각 유형의 최종 에이전트 수를 포함 할 빈 목록
    outcomeA = []
    outcomeB = []
    # "실행"횟수에 대해 정의 된 공격 범위 및 반복 제한으로 시뮬레이션 반복
    for i in range(0,runs):
        # 각 시뮬레이션 실행 전 전장을 초기화해야합니다.
        battlefield = initBattlefield(populationSizeA = 1000,populationSizeB = 950,battlefieldArr = battlefield)
        # 시뮬레이션 실행
        results = simulationRun(iterationLimit=iterationLimit,attackRange = attackRange,showPlot = False)
        # 관련 결과 목록에 결과 추가
        outcomeA.append(results[0][iterationLimit-1])
        outcomeB.append(results[1][iterationLimit-1])
    # 두 개의 하위 목록이있는 목록에 결과 반환
    return([outcomeA,outcomeB])

아래에서는 히스토그램 플로팅 기능을 구현합니다.

# 히스토그램을 그리는 함수
def plotHistogram(plotTitle,resultsA,resultsB):
    from matplotlib import pyplot
    pyplot.figure(figsize = (12,12))
    pyplot.title(plotTitle, fontsize = 24)
    pyplot.xlabel("number of agents still alive", fontsize = 20)
    pyplot.ylabel("absolute frequency", fontsize = 20)
    pyplot.xticks(fontsize = 16)
    pyplot.yticks(fontsize = 16)
    ax = pyplot.subplot()
    ax.hist(resultsA, bins=20, histtype="bar",color="red",label="agent A",alpha=0.25)
    ax.hist(resultsB, bins=20, histtype="bar",color="blue",label="agent B",alpha=0.25)
    ax.legend(fontsize=16)

마지막으로 아래 코드는 시인성 테스트를 실행하고 결과를 플로팅합니다.

results = sensitivityTest(iterationLimit = 50,attackRange = 5,runs = 50)
plotHistogram(plotTitle = "distribution of agents",resultsA = results[0], resultsB = results[1])

이 결과는 공격 범위 5에 대한 것입니다.

이 접근 방식을 사용하면 매우 고급 에이전트 기반 시뮬레이션 모델을 개발할 수 있습니다.

You May Also Like

Leave a Reply

Leave a Reply

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.