2016-08-03 38 views
2

ich GLPK mit Julia benutzen und mit den methods von spencerlyon geschriebenJulia - Senden GLPK.Prob an Arbeitern

sendto(2, lp = lp) #lp is type GLPK.Prob 

Aber ich kann nicht scheinen, eine Art GLPK.Prob zwischen Arbeitern zu senden. Jedes Mal, wenn ich versuche, eine Art GLPK.Prob zu senden, wird es ‚geschickt‘ und rufe

remotecall_fetch(2, whos) 

bestätigt, dass die GLPK.Prob

gesendet wurden

Das Problem erscheint, wenn ich versuche, es zu lösen, indem

Aufruf
simplex(lp) 

der Fehler

GLPK.GLPKError("invalid GLPK.Prob") 

erscheint. Ich weiß, dass die GLPK.Prob ist nicht ursprünglich ein ungültiges GLPK.Prob und wenn ich den GLPK.Prob Typen explizit auf einem anderen Arbeiter, fx Arbeiter 2, rief simplex läuft gerade fein

Dies ist ein Problem zu konstruieren entscheiden, Der GLPK.Prob wird von einem benutzerdefinierten Typ von mir generiert, der ein bisschen auf der schweren Seite ist

tl; dr Gibt es möglicherweise einige Typen, die nicht zwischen Arbeitern ordnungsgemäß gesendet werden können?

aktualisieren

Ich sehe jetzt, dass

remotecall_fetch(2, simplex, lp) 

Aufruf wird die oben GLPK Fehler zurück

Außerdem habe ich gerade bemerkt, dass das GLPK Modul ein Verfahren

genannt bekommen hat
GLPK.copy_prob(GLPK.Prob, GLPK.Prob, Int) 

aber deep (und schon gar nicht kopieren) wird nicht funktionieren, wenn das Kopieren eines GLPK.Prob

Beispiel

function create_lp() 
    lp = GLPK.Prob() 

    GLPK.set_prob_name(lp, "sample") 
    GLPK.term_out(GLPK.OFF) 

    GLPK.set_obj_dir(lp, GLPK.MAX) 

    GLPK.add_rows(lp, 3) 
    GLPK.set_row_bnds(lp,1,GLPK.UP,0,100) 
    GLPK.set_row_bnds(lp,2,GLPK.UP,0,600) 
    GLPK.set_row_bnds(lp,3,GLPK.UP,0,300) 

    GLPK.add_cols(lp, 3) 

    GLPK.set_col_bnds(lp,1,GLPK.LO,0,0) 
    GLPK.set_obj_coef(lp,1,10) 
    GLPK.set_col_bnds(lp,2,GLPK.LO,0,0) 
    GLPK.set_obj_coef(lp,2,6) 
    GLPK.set_col_bnds(lp,3,GLPK.LO,0,0) 
    GLPK.set_obj_coef(lp,3,4) 

    s = spzeros(3,3) 
    s[1,1] = 1 
    s[1,2] = 1 
    s[1,3] = 1 
    s[2,1] = 10 
    s[3,1] = 2 
    s[2,2] = 4 
    s[3,2] = 2 
    s[2,3] = 5 
    s[3,3] = 6 

    GLPK.load_matrix(lp, s) 

    return lp 
end 

Dies wird eine lp zurückkehren :: GLPK.Prob(), die 733,33 zurückkehren wird, wenn

läuft
simplex(lp) 
result = get_obj_val(lp)#returns 733.33 

führt jedoch

addprocs(1) 
remotecall_fetch(2, simplex, lp) 

tun in dem Fehler über

+0

Können Sie ein reproduzierbares Beispiel veröffentlichen? –

Antwort

1

Es sieht wie das Problem ist, dass Ihr lp Objekt einen Zeiger enthält.

julia> lp = create_lp() 
GLPK.Prob(Ptr{Void} @0x00007fa73b1eb330) 

Leider mit Zeigern und Parallelverarbeitung arbeiten, ist schwierig - wenn unterschiedliche Prozesse unterschiedliche Speicherplätze haben dann ist es nicht klar ist, welche Speicheradresse der Prozess an, um aussehen sollte auf den Speicher zuzugreifen, dass der Zeiger zu.Diese Probleme können überwunden werden, aber sie erfordern offensichtlich individuelle Arbeit für jeden Datentyp, der diese Zeiger beinhaltet, siehe this GitHub-Diskussion für mehr.

Also mein Gedanke wäre, dass wenn Sie auf den Zeiger auf den Arbeiter zugreifen möchten, Sie es einfach auf diesem Arbeiter erstellen können. Z.B.

using GLPK 
addprocs(2) 

@everywhere begin 
    using GLPK 
    function create_lp() 
     lp = GLPK.Prob() 

     GLPK.set_prob_name(lp, "sample") 
     GLPK.term_out(GLPK.OFF) 

     GLPK.set_obj_dir(lp, GLPK.MAX) 

     GLPK.add_rows(lp, 3) 
     GLPK.set_row_bnds(lp,1,GLPK.UP,0,100) 
     GLPK.set_row_bnds(lp,2,GLPK.UP,0,600) 
     GLPK.set_row_bnds(lp,3,GLPK.UP,0,300) 

     GLPK.add_cols(lp, 3) 

     GLPK.set_col_bnds(lp,1,GLPK.LO,0,0) 
     GLPK.set_obj_coef(lp,1,10) 
     GLPK.set_col_bnds(lp,2,GLPK.LO,0,0) 
     GLPK.set_obj_coef(lp,2,6) 
     GLPK.set_col_bnds(lp,3,GLPK.LO,0,0) 
     GLPK.set_obj_coef(lp,3,4) 

     s = spzeros(3,3) 
     s[1,1] = 1 
     s[1,2] = 1 
     s[1,3] = 1 
     s[2,1] = 10 
     s[3,1] = 2 
     s[2,2] = 4 
     s[3,2] = 2 
     s[2,3] = 5 
     s[3,3] = 6 

     GLPK.load_matrix(lp, s) 

     return lp 
    end 
end 

a = @spawnat 2 eval(:(lp = create_lp())) 
b = @spawnat 2 eval(:(result = simplex(lp))) 
fetch(b) 

Lesen Sie die Dokumentation unten auf @spawn für weitere Informationen über die Verwendung es, da es ein wenig gewöhnungs nehmen.



Die Makros @spawn und @spawnat sind zwei der Werkzeuge, die Julia zuweisen Aufgaben für die Arbeitnehmer zur Verfügung stellt. Hier ein Beispiel:

julia> @spawnat 2 println("hello world") 
RemoteRef{Channel{Any}}(2,1,3) 

julia> From worker 2: hello world 

Beide Makros wird eine expression auf einem Arbeitsprozess bewerten. Der einzige Unterschied zwischen den beiden ist, dass Sie mit @spawnat auswählen können, welcher Mitarbeiter den Ausdruck auswerten wird (im obigen Beispiel wird Arbeiter 2 angegeben), während mit @spawn ein Arbeiter automatisch ausgewählt wird, je nach Verfügbarkeit.

In dem obigen Beispiel hatten wir einfach Arbeiter 2 die Funktion println ausführen. Es gab nichts Interessantes, um davon zurückzukommen oder davon zurückzukommen. Oft jedoch wird der Ausdruck, den wir dem Arbeiter schicken, etwas ergeben, das wir abrufen wollen. Beachten Sie in dem obigen Beispiel, wenn wir @spawnat aufgerufen, bevor wir den Ausdruck von Arbeiter 2, kamen, sahen wir folgendes:

RemoteRef{Channel{Any}}(2,1,3) 

Dies zeigt an, dass das @spawnat Makro ein RemoteRef Typ Objekt zurück. Dieses Objekt enthält wiederum die Rückgabewerte aus unserem Ausdruck, der an den Worker gesendet wird. Wenn wir diese Werte abrufen möchten, können wir zuerst die RemoteRef, die @spawnat zurückgibt, einem Objekt zuweisen und dann die fetch()-Funktion verwenden, die auf einem RemoteRef-Objekt arbeitet, um die Ergebnisse aus einer an einem Worker ausgewerteten Auswertung abzurufen.

julia> result = @spawnat 2 2 + 5 
RemoteRef{Channel{Any}}(2,1,26) 

julia> fetch(result) 
7 

Der Schlüssel zum @spawn effektiv Verständnis über die Natur hinter den expressions, dass es arbeitet, einsetzen zu können. Die Verwendung von @spawn zum Senden von Befehlen an Worker ist etwas komplizierter, als wenn Sie direkt eingeben, was Sie eingeben würden, wenn Sie einen "Interpreter" auf einem der Worker ausführen oder nativ Code ausführen würden. Angenommen, wir möchten @spawnat verwenden, um einer Variablen auf einem Worker einen Wert zuzuweisen. Wir könnten versuchen:

@spawnat 2 a = 5 
RemoteRef{Channel{Any}}(2,1,2) 

Hat es funktioniert? Nun, lassen Sie uns sehen, indem wir Arbeiter 2 versuchen, a zu drucken.

julia> @spawnat 2 println(a) 
RemoteRef{Channel{Any}}(2,1,4) 

julia> 

Nichts ist passiert. Warum? Wir können dies genauer untersuchen, indem wir fetch() wie oben verwenden. fetch() kann sehr praktisch sein, weil es nicht nur erfolgreiche Ergebnisse, sondern auch Fehlermeldungen abrufen wird. Ohne sie wissen wir vielleicht nicht einmal, dass etwas schiefgelaufen ist.

julia> result = @spawnat 2 println(a) 
RemoteRef{Channel{Any}}(2,1,5) 

julia> fetch(result) 
ERROR: On worker 2: 
UndefVarError: a not defined 

Die Fehlermeldung besagt, dass a nicht auf 2. Arbeitnehmer definiert ist, aber warum?Der Grund dafür ist, dass wir unsere Zuweisungsoperation in einen Ausdruck umbrechen müssen, den wir dann @spawn verwenden, um den Worker zu bewerten. Unten ist ein Beispiel, mit Erklärung folgenden:

julia> @spawnat 2 eval(:(a = 2)) 
RemoteRef{Channel{Any}}(2,1,7) 

julia> @spawnat 2 println(a) 
RemoteRef{Channel{Any}}(2,1,8) 

julia> From worker 2: 2 

Die :() Syntax ist das, was Julia expressions zu bezeichnen, verwendet. Wir verwenden dann die eval() Funktion in Julia, die einen Ausdruck auswertet, und wir nutzen die @spawnat Makro anzuweisen, dass der Ausdruck auf Arbeitnehmer bewertet 2.

konnten wir erreichen auch das gleiche Ergebnis wie:

julia> @spawnat(2, eval(parse("c = 5"))) 
RemoteRef{Channel{Any}}(2,1,9) 

julia> @spawnat 2 println(c) 
RemoteRef{Channel{Any}}(2,1,10) 

julia> From worker 2: 5 

Dieses Beispiel demonstriert zwei zusätzliche Begriffe. Zuerst sehen wir, dass wir auch einen Ausdruck mit der Funktion parse() erstellen können, die für eine Zeichenfolge aufgerufen wird. Zweitens sehen wir, dass wir Klammern verwenden können, wenn wir @spawnat aufrufen, in Situationen, in denen dies unsere Syntax klarer und überschaubarer machen könnte.

+0

Danke, das ist nahe, was ich getan habe. Ich hole jedes relevante Array aus dem lp und übertrage es und lasse eine create_lp() -Methode für jeden Worker definieren, der ein lp erzeugt. Ich würde immer noch gerne einen Weg finden, die copy_prob-Methode zu verwenden und den Zeiger (wenn das etwas ist, was du tun kannst) an einen anderen Arbeiter zu verschieben, da es immer diese Suche nach Eleganz gibt. – isebarn

+0

@isebarn Sicher Sache. Beachten Sie auch, dass wenn Sie 'remotecall_fetch()' verwenden und dann der Funktion Argumente übergeben, die von Ihnen angegebenen Objekte standardmäßig aus dem Bereich des Prozesses kommen, der 'remotecall_fetch()' aufruft und diese Objekte dann an die Prozess wird aktiviert, anstatt "remotecall" nativ Argumente im Bereich des Worker-Prozesses zu verwenden. Dies kann dann auch zu diesen Arten von Serialisierungsfehlern führen. –