1

私はMATLABでエージェントベースのシミュレーションを書いています。このシミュレーションでは、エージェントがラウンドでゲームをプレイし、お互いに物を売っています。ほとんどすべて(エージェント、アイテム、場所、コントラクトなど)は、MATLABのOOP機能を使用してオブジェクトとして実装されます。

毎ラウンド、シミュレーションのスナップショットを取り、それをディスクに保存して、後でシミュレーションがどのように開発されたかを分析できるようにします。今私の質問はそうするための最良の方法は何でしょうか?

私の現在の考えは、メインループがすべてのエージェントを呼び出し、そのステータスを報告するように要求することです(たとえば、所有しているプロパティのアイテムの数、契約上の義務、アカウントの残高...必要に応じて、エージェントはオブジェクトを呼び出します彼らは自分のステータスを所有して要求し、その情報をレポートに含めます)。私のアイデアは、エージェントのレポートを文字列(おそらくXML形式)にすることでした。次に、すべてのレポートをタイムスタンプと一緒に追加し、テキストファイルの最後に追加します

しかし、私はこのようなことをしたことがないので、これが良いアプローチであるかどうかはわかりません。後で簡単に分析できる形式のデータを用意すること以外の私の主な関心事は、スナップショットを作成してディスクに書き込む速度です。私のシミュレーションはかなり大きいので、ラウンドごとに大量のデータが保存されると思います。

代替案は次のとおりです。

  1. すべてをデータベースに保存します。しかし、データベースへのアクセスはテキストファイルに比べてかなり遅いと思います。また、各エージェントが所有するオブジェクトの数は変化する可能性があるため、データベースの構造についてもよくわかりません。
  2. .matファイルの使用。しかし、それらが簡単に拡張可能かどうか、そして変化する構造(つまり、ラウンドごとに異なるアイテムを所有するエージェント)にどのように対処するかはわかりません

コメントや提案をありがとう!

4

3 に答える 3

3

。マット

Matlabをターゲットにしているので、まず.matファイルから始めます。いつかデータをMatlabにリロードする必要がある場合、これはXMLストレージと比較して望ましいソリューションです。スナップショットはセル配列で表現する必要があります。構造の変更について心配する必要はありません。たとえば、エージェントがラウンドごとに異なるアイテムを所有している場合、ラウンドごとのアイテムは別の(ネストされた)セル配列として格納できます。

データベース

スナップショットがMatlabから再度読み取られない場合は、SQLインターフェイスを検討してください。これにより、永続層のパフォーマンスを拡張できます。SQLiteを採用することから始めて、あるメトリックの下でより良いパフォーマンスが必要であることがわかった場合は、より「深刻な」DBMSに移行することができます。

データベースの構造についての疑問については、確かにスナップショットの構造が必要です。データベースアプリケーションを適切に設計しても、スナップショットの可変コンテンツを管理できないとは思いません。

カスタム

実際にI/Oを多用するシナリオで、データを排他的に追加することになった場合、専用のソリューションは妥当な投資です。柔軟性が失われ、後悔する可能性がありますが、最高のものが必要です。ただし、XMLボートに飛び乗らないことをお勧めします。これは、世の中で最もコンパクトなソリューションではないため、非常に大きなデータセットで問題が発生する可能性があります。独自の形式を設計するのではなく、JSONを使用したいと思います。JSONはコンパクトで用途が広く、Matlabでの解析に役立つライブラリがおそらく存在します。いいえ、実際にあります

于 2012-06-12T16:49:23.003 に答える
2

私も両方のオプションを試しました。

データがほとんどない場合を除いて、個別のマットファイルを保存することはお勧めしません。一意の名前を考え出し、それを収集するのは非常に面倒です。計算を並列化するときは放っておいてください。ファイルに同時にアクセスすると問題が発生する可能性があります。

データベースの場合、MATLABのMySqlサーバーとmymコマンドの組み合わせが好きです。サーバー。複数のプロセスから同時にアクセスできるようにします(並列化に不可欠)。Mymを使用すると、matlabオブジェクトをデータベースのblobフィールドに直接書き込むことができ、書き換えを節約できます。

SQLiteが上で述べたように、私はそれをいじくり回してきました。しかし、かなりイライラしました。オブジェクトを自分でシリアル化する必要があります。また、複数のプロセスがデータベースにアクセスすることには問題があります。

于 2012-06-12T22:02:19.817 に答える
1

この回答では、私が最終的に実装したソリューションを要約します。それは私の特定の問題に合うように特別に設定されているので、代替オプションについてはLucaGerettiとbdecafによる回答も参照することをお勧めします。

私が実装したもの

SQLiteはデータを1つのファイルに保存し、セットアップと処理が簡単だったため、データベースとしてSQLiteを選択しました。(sqlite3とsqlite-jdbc-3.7.2ドライバーを使用すると、インストールするものはそれほど多くなく、データベースはすべて1つの単純なファイルに含まれます。)

Matlab Database Toolboxは、シミュレーション中に作成されたMatlabから大量のデータをエクスポートするには遅すぎることが判明しました。したがって、データスナップショットをcsvファイルにダンプする「DataOutputManager」クラスを作成しました。

シミュレーション後、DataOutputManagerはSQLコマンドを含む2つのバッチファイルと2つのSQLテキストファイルを作成し、バッチファイルを実行します。最初のバッチファイルは、sqlite3.exewww.sqlite.org)を実行し、最初のテキストファイルでSQLコマンドを指定してSQLiteデータベースを作成します。

データベースが作成された後、sqlite3は、2番目のバッチとテキストファイルを使用して、csvファイルからデータベースにデータをインポートするように指示されます。これは「きれいな」ソリューションではありませんが、データをcsvに書き込んでから、sqlite3を使用してこれらのファイルをデータベースにインポートする方が、DatabaseToolboxを使用するよりもはるかに高速でした。(xmlファイルを使用している人のことを聞いたことがあります)

シミュレーションデータがデータベースにアップロードされた後、データベースツールボックスをjdbcドライバー( sqlite-jdbc-3.7.2 )と一緒に使用して、SQLクエリをデータベースに送信します。これらのクエリから返されるデータはごくわずかであるため、DatabaseToolboxはここでのボトルネックではありません。

これらすべてを(Windows 7で)設定するには、検索とテストの割り当てが必要でした。完璧ではありませんが、誰かが似たようなことをしたい場合は、次のスニペットが役立つことを願っています。

SQLiteデータベースの作成とcsvからデータベースへのデータのインポート:

sqlite3を使用してデータベースを作成する最初の.batファイルは次のように構成されています。

sqlite3 DatabaseName.db <DatabaseNameStructure.sql

最初のテキストファイル(.sql)はDatabaseNameStructure.sqlと呼ばれ、次のように構成されています。

始める; テーブルTable1Name(Column1Name real、Column2Name real、Column2Name real);を作成します。テーブルTable2Name(Column1Name real、Column2Name real、Column2Name real);を作成します。専念;

sqlite3がcsvファイルをデータベースにアップロードできるようにする2番目の.batファイルは、次のように構成されています。

sqlite3 DatabaseName.db <uploadCsvToDatabaseName.sql

2番目のテキストファイル(.sql)はuploadCsvToDatabaseName.sqlと呼ばれ、次のように構成されています。

.separator "、"

.import Table1Data.csv Table1Name

.import Table2Data.csv Table2Name

。出口

これを機能させるには、システムパスにsqlite3.exeを含める必要があります(例:C:\ Windows \ System32に保存)。data / csvの設定に従ってMatlabでスティングを作成し、fprintf()を使用して上記の形式でファイルに書き込みます。次に、winopen()を使用してMatlabからbatファイルを実行します。

jdbcドライバーを使用してMatlabをSQLiteデータベースに接続します。

ブライアンダウニングによる次のビデオは、私がこれを開発するのに役立ちました: http ://www.youtube.com/watch?v= 5QNyOe79l-s

データベースに接続し、シミュレーション結果に対してすべての分析を実行するMatlabクラス(「DataAnalyser」)を作成しました。データベースとの通信を設定するためのクラスコンストラクタと接続関数は次のとおりです。(実装の中でそれほど重要ではない部分を切り取りました)


 function Analyser=DataAnalyser()

        % add SQLite JDBC Driver to java path
        Analyser.JdbcDriverFileName='sqlite-jdbc-3.7.2.jar';

        % Ask User for Driver Path
        [Analyser.JdbcDriverFileName, Analyser.JdbcDriverFilePath] = uigetfile( {'*.jar','*.jar'},['Select the JDBC Driver file (',Analyser.JdbcDriverFileName,')']);
        Analyser.JdbcDriverFilePath=[Analyser.JdbcDriverFilePath,Analyser.JdbcDriverFileName];

        JavaDynamicPath=javaclasspath('-dynamic'); % read everything from the dynamic path
        if~any(strcmp(Analyser.JdbcDriverFilePath,JavaDynamicPath))           
            disp(['Adding Path of ',Analyser.JdbcDriverFileName,' to java dynamic class path'])
            javaaddpath(Analyser.JdbcDriverFilePath);
        else
            disp(['Path of ',Analyser.JdbcDriverFileName,' is already part of the java dynamic class and does not need to be added']);
        end


        Analyser.JdbcDriver='org.sqlite.JDBC';
        % Ask User for Database File
        [Analyser.DbFileName, Analyser.DbFilePath] = uigetfile( '*.db','Select the SQLite DataBase File ');
        Analyser.DbFilePath=[Analyser.DbFilePath,Analyser.DbFileName];

        Analyser.DbURL=sprintf('jdbc:sqlite:%s',Analyser.DbFilePath);
        % Set Timeout of trying to connect with Database to 5 seconds
        logintimeout(Analyser.JdbcDriver,5);               
    end

    function [conn,isConnected]=connect(Analyser)
        % Creates connection to database.            
        Analyser.Connection=database(Analyser.DbFilePath,'','',Analyser.JdbcDriver,Analyser.DbURL);
        conn=Analyser.Connection;
        isConnected=isconnection(Analyser.Connection);                  
    end

接続されたSQLiteデータベースからMatlabにデータを取得する

また、SQLクエリが与えられたときにデータベースからデータを取得するDataAnalyserの関数も作成しました。私は2つの理由でそれの主要な部分をここに投稿しています。

  1. すべてのデータを一度にインポートするのではなく、この関数のように部分的にインポートすると、データのインポートが高速になります。

  2. Mathworksには、Database Toolbox(cursor.fetch)のドキュメントでこれを行う方法の提案があります。ただし、jdbcとSQLiteを使用すると、バグが原因でエラーが発生します。

Mathworksサポートからの引用:

レコードセットの最後にいる場合、SQLite JDBCドライバーは、レコードセットに関する特定のメタデータのクエリを許可しないことを以前に確認しました。ただし、JDBC仕様によれば、これは許可されるべきです。

この関数はその問題を回避します:


function OutputData=getData(Analyser,SqlQuery,varargin)
        % getData(Analyser,SqlQuery)
        % getData(Analyser,SqlQuery, setdbprefsString)
        % getData(Analyser,SqlQuery,RowLimitPerImportCycle)
        % getData(Analyser,SqlQuery,RowLimitPerImportCycle,setdbprefsArg1String,setdbprefsArg2String)
        % getData(Analyser,SqlQuery,[],setdbprefsArg1String,setdbprefsArg2String)
        % 
        % RowLimitPerImportCycle sets the Limit on howmany Data rows 
        % are imported per cycle. 
        % Default is RowLimitPerImportCycle = 6000
        %
        % setdbprefsArg1String Default 'datareturnformat' 
        % setdbprefsArg2String Default 'numeric'
        % Hence setdbprefs('datareturnformat','numeric') is the Default
        %            
        % function is partially based on cursor.fetch Documentation for 
        % Matlab R2012b:
        % http://www.mathworks.de/de/help/database/ug/cursor.fetch.html
        % Example #6 as of 10.Oct.2012
        % The Mathworks' cursor.fetch Documentation mentioned above had
        % some errors. These errors were (among other changes)
        % corrected and a bug report was send to Mathworks on 10.Oct.2012

        if isempty(Analyser.Connection)                
            disp('No open connection to Database found.')
            disp(['Trying to connect to: ',Analyser.DbFileName])
            Analyser.connect
        end

        % Get Setting
        if nargin>2
            RowLimitPerImportCycle=varargin{1};
        else
            RowLimitPerImportCycle=[];
        end
        if ~isnumeric(RowLimitPerImportCycle) || isempty(RowLimitPerImportCycle)
            %Default
            RowLimitPerImportCycle=5000;
        end

        if nargin>4
            setdbprefsArg1String=varargin{2};
            setdbprefsArg2String=varargin{3};
        else
            setdbprefsArg1String='';
            setdbprefsArg2String='';
        end
        if ischar(setdbprefsArg1String) && ~isempty(setdbprefsArg1String) && ischar(setdbprefsArg2String) && ~isempty(setdbprefsArg2String)
            setdbprefs(setdbprefsArg1String,setdbprefsArg2String)
        else
            %Default
            setdbprefs('datareturnformat','numeric');
        end



        % get Curser
        curs=exec(Analyser.Connection,SqlQuery);
        if ~isempty(curs.Message)
            warning('Model:SQLMessageGetData',[curs.Message, '/n while executing SqlQuery: ',SqlQuery])
        end

        % import Data
        FirstRow = 1;
        LastRow = RowLimitPerImportCycle;
        firstLoop=true;

        while true

            curs = fetch(curs,RowLimitPerImportCycle);
            if rows(curs)==0
                if firstLoop == true
                    OutputData=[];
                end
                break
            end

            AuxData = curs.Data;
            numImportedRows = size(AuxData,1);
            if numImportedRows < RowLimitPerImportCycle
                OutputData(FirstRow:LastRow-(RowLimitPerImportCycle-numImportedRows), :) = AuxData;
            else
                OutputData(FirstRow:LastRow, :) = AuxData;
            end

            FirstRow = FirstRow + RowLimitPerImportCycle;
            LastRow = LastRow + RowLimitPerImportCycle;
            firstLoop=false;

            if rows(curs)<RowLimitPerImportCycle
                break
            end
        end

    end

于 2012-12-19T20:53:28.690 に答える