2

Python で SimPy を使用して、ユーザーが csv ファイルで入力したスケジュールに基づいてリソースを利用できるようにする必要がある離散イベント シミュレーションを作成しています。目的は、1 日の異なる時間帯に利用可能な同じリソース (スタッフなど) の異なる数を表すことです。私が知る限り、これはベースの SimPy で利用できるものではありません - リソースの優先順位のようなものです。

私はこれを機能させることができ、その方法を示すために以下のコードを含めました。しかし、SimPy でこの機能を実現するためのより良い方法があるかどうか、コミュニティに尋ねたかったのです。

以下のコードは、毎日の開始時に、リソースが利用できないと想定されている時間帯にリソースを要求することで機能します。リソースを確実に取得するために、はるかに高い優先度を使用します。その後、リソースは適切なタイミングで解放され、他のイベント/プロセスで使用できるようになります。私が言うように、それは機能しますが、リソースの正確な真の可用性を確保するために多くのダミープロセスが機能しているため、無駄に思えます. 改善につながるコメントを歓迎します。

したがって、csv は次のようになります。

Number  time
0        23
50       22
100      17
50       10
20       8
5        6

ここで Number は、定義された時間に利用可能になるスタッフの数を表します。例: 6 時から 8 時までは 5 名、8 時から 10 時までは 20 名、10 時から 17 時までは 50 名というように、その日の終わりまでスタッフが勤務します。

コード:

import csv
import simpy

# empty list ready to hold the input data in the csv
input_list = []

# a dummy process that "uses" staff until the end of the current day
def take_res():
    req = staff.request(priority=-100)
    yield req  # Request a staff resource at set priority
    yield test_env.timeout(24 - test_env.now)

# A dummy process that "uses" staff for the time those staff should not 
# be available for the real processes     
def request_res(delay, avail_time):
    req = staff.request(priority=-100)
    yield req  # Request a staff resource at set priority
    yield test_env.timeout(delay)
    yield staff.release(req)
    # pass time it is avail for
    yield test_env.timeout(avail_time)
    test_env.process(take_res())

# used to print current levels of resource usage
def print_usage():
    print('At time %0.2f %d res are in use' % (test_env.now, staff.count))
    yield test_env.timeout(0.5)
    test_env.process(print_usage())

# used to open the csv and read the data into a list
with open('staff_schedule.csv', mode="r") as infile:
    reader = csv.reader(infile)
    next(reader, None)  # ignore header
    for row in reader:
        input_list.append(row[:2])

    # calculates the time the current number of resources will be 
    # available for and adds to the list 

    i = 0
    for row in the_list:
        if i == 0:
            row.append(24 - int(input_list[i][1]))
        else:
            row.append(int(input_list[i-1][1]) - int(input_list[i][1]))

        i += 1

    # converts list to tuple of tuples to prevent any accidental 
    # edits from this point in
    staff_tuple = tuple(tuple(row) for row in input_list)
    print(staff_tuple)

# define environment and creates resources   
test_env = simpy.Environment()
staff = simpy.PriorityResource(test_env, capacity=sum(int(l[0]) for l in staff_tuple))

# for each row in the tuple run dummy processes to hold resources 
# according to schedule in the csv
for item in the_tuple:
    print(item[0])
    for i in range(int(item[0])):
        test_env.process(request_res(int(item[1]), int(item[2])))

# run event to print usage over time
test_env.process(print_usage())

# run for 25 hours - so 1 day
test_env.run(until=25)
4

3 に答える 3

0

これが私のアプリケーションで解決した方法です。完璧ではありませんが、Python と SimPy の基本的なスキルを考えると、私ができる最善の方法でした。

その結果、必要な時間に適切な数のアドバイザが利用可能になります。

まず、ストアを定義し、シミュレーション内に存在するアドバイザ インスタンスの総数と同じになるようにキャパシティを設定します。

self.adviser_store = simpy.FilterStore(self.env,
capacity=self.total_ad_instances)

必要な Adviser クラスのインスタンスは、簡潔にするために含めていない初期化ステップで作成されます。私は実際に JSON ファイルを使用して個々の advisor インスタンスをカスタマイズし、それらをリストに入れます。

以下のクラス定義の実行パラメーターは、実際には、シミュレーションの現在の実行に関連するすべての情報を含む別のクラスです。たとえば、シミュレーションの開始日と終了日が含まれています。したがって、self.start_date は、アドバイザーが作業を開始する日付を定義します。self.run.start_date は、シミュレーションの開始日です。

class Adviser(object):

    def __init__(self, run, id_num, start_time, end_time, start_date,  end_date):

    self.env = run.env
    self.run = run
    self.id_num = id_num        
    self.start_time = start_time
    self.end_time = end_time
    self.start_date = datetime.datetime.strptime(start_date, '%Y, %m, %d')
    self.end_date = datetime.datetime.strptime(end_date, '%Y, %m, %d')
    self.ad_type = ad_type

    self.avail = False
    self.run.env.process(self.set_availability())  

ご覧のとおり、アドバイザ クラスを作成すると、可用性を設定するプロセスも開始されます。以下の例では、特定の日付範囲で毎日同じ空室状況を設定するように簡略化しています。もちろん、日付/日などに応じて異なる可用性を設定することもできます.

def set_availability(self):

    start_delay = self.start_time + (self.start_date - self.run.start_date).total_seconds()/3600 # this returns the time in hours until the resource becomes available and is applied below.
    end_delay = self.end_time + (self.start_date - self.run.start_date).total_seconds()/3600
    repeat = (self.end_date - self.start_date).days + 1  # defines how man days to repeat it for

    for i in range(repeat):

        start_delayed(self.run.env, self.add_to_store(), start_delay)
        start_delayed(self.run.env, self.remove_from_store(), end_delay)
        start_delay += 24
        end_delay += 24

    yield self.run.env.timeout(0)


def add_to_store(self):

    self.run.ad_avail.remove(self)  # take adviser from a list
    self.run.adviser_store.put(self)  # and put it in the store
    yield self.run.env.timeout(0)

def remove_from_store(self):        

    current_ad = yield self.run.adviser_store.get(lambda item: item.id_num == self.id_num)  # get itself from the store 
    self.run.ad_avail.append(current_ad) # and put it back in the list
    yield self.run.env.timeout(0)

したがって、基本的に、顧客はストアからのみアドバイザーを要求でき、アドバイザーは特定の時間にのみストアにいます。残りの時間は、シミュレーションの現在の実行に添付されたリストにあります。

ここにはまだ落とし穴があると思います。アドバイザ オブジェクトは、使用できなくなる予定のときに使用中の可能性があります。これがまだ発生するかどうか、または発生した場合の影響に気づいていません。

于 2016-04-21T09:48:16.923 に答える
0

SimPy 関連

多分あなたは使うことができますPreemptiveResourceこの例を見てください)。これにより、リソースごとに 1 つのブロッカー プロセスのみが必要になります。

Python関連

  • コードを文書化します。take_res()との目的は何request_res()ですか? priority=-100(とにかく、両方の関数が を使用するのはなぜですか?)
  • より良い名前を使用してください。the_listまたはthe_tupleあまり役に立ちません。
  • あなたの代わりにthe_list.append(row[0], row[1])行うことができますthe_list.append(row[:2])
  • リストのリストをタプルのタプルに変換するのはなぜですか? 私が見る限り、メリットはあります。しかし、それは余分なコードを追加するため、プログラミングエラーの余分な混乱と余分な可能性があります.
  • with open(file)できるだけ早くブロックを離れる必要があります (あなたの場合、最初の 4 行の後)。必要以上にファイルを開いたままにしておく必要はありません。すべての行の反復処理が完了したら、ファイルは不要になります。
于 2015-10-26T18:41:27.023 に答える