2013-06-20 13 views
5

Voglio sapere se è possibile chiamare la funzione di ottimizzazione statistica r (qui voglio usare regnoud) da C# mentre la funzione da ottimizzare è scritta in C#. Ho trovato la libreria RDotNet ma con essa non riesco a valutare la funzione C# da R. In altri termini, il problema è che R mentre l'ottimizzazione ha bisogno di valutare la funzione ma non può farlo perché la funzione è nel codice C#.È possibile chiamare una funzione statistica R per ottimizzare la funzione C#

c'è qualche soluzione come l'uso di .dll o altre librerie?
grazie.

+0

Perché non riscrivere la funzione in R? – agstudy

+0

@agstudy solo perché la (e) funzione (i) è (sono) scritta in più di 50 classi e più di 10000 linee. :( – MBS

+0

Puoi condividere un esempio di una di queste funzioni per fornire un contesto aggiuntivo? Puoi condividere il tuo codice C# che non funziona pure. –

risposta

0

http://www.codeproject.com/Articles/25819/The-R-Statistical-Language-and-C-NET-Foundations

Partenza qualcosa di simile. Ora non so molto di C# quindi se dico qualcosa che è impossibile per favore non farmi del male:

Prova a salvare la funzione che vuoi ottimizzare come carattere in C#, e inviarla a R usando questo Libreria StatConnector (ne ho un'esperienza limitata). Diciamo che hai salvato l'equazione come 'z' in R, puoi quindi chiamare i tuoi script R con 'get (z)' come una delle variabili.

+1

Grazie, ma sembra andare nella direzione opposta: chiamare R da C# Sono interessato a come chiamereste C# (o qualsiasi altro codice gestito) da R. –

9

Ho fatto questo prima di usare un approccio un po 'contorto ma funziona!

Innanzitutto, è necessario creare una DLL C# contenente la propria funzione. Puoi farlo in Visual Studio selezionando "libreria di classi" come opzione quando crei un nuovo progetto. Il codice nel file .cs dovrebbe essere simile

namespace MyNamespace 
{ 
    //expose an interface with functions to be called from R 
    public interface MyInterface 
    { 
     string MyFunction(string name); 
    } 

    //create a class that implements the above interface 
    public class MyClass : MyInterface 
    { 
     public string MyFunction(string name) 
     { 
     return "Hello " + name; 
     } 
    } 

} 

Ora compilare il progetto e si otterrà un C# DLL. Questa DLL è una DLL gestita che è diversa da una DLL C/C++ nativa. Non può essere direttamente consumato da linguaggi non. NET e quindi deve essere esposto come un oggetto COM. È possibile farlo in uno dei due modi

  1. In Visual Studio, è possibile andare alle proprietà del progetto, clicca su "informazioni Assembly" pulsante sotto scheda "Applicazioni" e selezionare la casella di controllo che dice "Make assembly COM visibile ".
  2. In alternativa, è possibile utilizzare regasm.exe che può essere trovato nella cartella di installazione .net per registrare la DLL come componente COM. Ecco un link che descrive questo processo. http://msdn.microsoft.com/en-us/library/tzat5yw6(v=vs.71).aspx. Il comando è in genere "regasm myTest.dll /tlb:myTest.tlb"

Il processo di registrazione COM ora avrà creato un file .tlb nella stessa cartella della DLL. Conserva questo file .tlb. Ne avremo bisogno nel passaggio successivo

Il passaggio successivo consiste nel creare una DLL C++ che possa chiamare la DLL COM. Questo passaggio è necessario perché R può chiamare direttamente una DLL C++ ma non può chiamare direttamente la COM (correggimi se ho torto o salta questo passaggio se conosci un modo migliore per chiamare COM da R). Il codice per il ++ DLL C in dllmain.cpp Si riporta di seguito (accertarsi di scorrerlo per vedere il codice completo)

#include "stdafx.h" 
#include <iostream> 

//import the .tlb file create by COM registration 

#import "path_to_Dll\MyDll.tlb" named_guids raw_interfaces_only 

    void _cdecl MyCPPFunction(char ** strName) 
    { 
     //Initialize COM 
     HRESULT hr = CoInitialize(NULL); 

     //create COM interface pointer 
     MyNamespace::MyInterfacePtr myPtr; 

     //create instance of COM class using the interface pointer 
     HRESULT hr2 = myPtr.CreateInstance(MyNamespace::CLSID_MyClass); 

     //create variable to hold output from COM. 
     BSTR output; 

     //call the COM function 
     myPtr->MyFunction(_bstr_t(strName[0]), &output); 

     //convert the returned BSTR from .net into char* 
     int length = (int) SysStringLen(output); 
     char *tempBuffer; 
     tempBuffer = (char *) malloc(1 + length); 
     WideCharToMultibyte(CP_ACP, 0, output, -1, tempBuffer, length, NULL, NULL); 
     tempBuffer[length] = '\0'; 

     //release interface 
     myPtr->Release(); 

     //uninitialize COM 
     if(hr == S_OK) 
     CoUninitialize(); 

     //set output in input for returning to R (this is weird but the only way I could make it work) 
     strName[0] = tempBuffer; 
    } 

Ora compilare il progetto e si otterrà una DLL C++. Siamo quasi arrivati ​​ora! Non arrenderti ancora :). Ora, hai una DLL C# esposta come un oggetto COM e una DLL C++ che può chiamare questo oggetto COM. Il passo finale è quello di chiamare la funzione C++ da R. Qui è il codice R per farlo

#load C++ DLL 
dyn.load("path_to_C++_DLL") 

#call function in C++ DLL and pass in a test name. 
# The C++ DLL will in turn call the C# function 
output <- .C("MyCPPFunction", as.character("Peter")) 

#print output 
print(output) 

e si dovrebbe vedere "Ciao Peter" visualizzato da C# nella console R! Un punto molto importante da notare.

Compilare sempre la DLL COM utilizzando l'opzione "qualsiasi CPU". Compila sempre la DLL C++ in modo che corrisponda alla tua installazione R! Ad esempio, se hai 32 bit R installati, assicurati di compilare la DLL C++ come DLL a 32 bit. Se hai 64 bit R installati e vuoi usarli, assicurati di compilare la tua DLL C++ come una DLL a 64 bit. Buona fortuna!

+0

Non ho provato ciò che @ user1 ha suggerito, ma sembra un modo per andare e una convoluzione impressionante. Probabilmente è meglio evitare COM se possibile, è più simile a una stampella legacy: dai un'occhiata a RInside e usa user1 suggerendo di chiamare C++ direttamente senza la deviazione COM. –

+1

@DieterMenne - Ho un altro metodo che evita COM ma è altrettanto complicato come la mia soluzione originale :) Esso comporta la creazione di una DLL in modalità mista che è fondamentalmente una DLL che contiene alcune funzioni pure di C++ e alcune funzioni di Visual C++ .Net. R può quindi chiamare la funzione C++ che a sua volta può chiamare la funzione Visual C++ che può chiamare C# DLL senza l'utilizzo di COM. Se qualcuno è interessato a saperne di più su C++/CLI, ecco un link a un articolo approfondito. http://www.codeproject.com/Articles/20466/C-CLI-Primer-Enter-the-World-of-NET-Power-Programm –

1

Se ho compreso chiaramente il tuo bisogno, hai un problema di ottimizzazione con un C# che chiama R richiamando C#.

Non penso che ci sia un modo per impostare una funzione di richiamata ("puntatore di funzione", "delegato" come possono essere chiamati a seconda di chi si parla) in R.NET attualmente. Probabilmente avrò esigenze molto simili e potrei contribuire a questo in futuro su R.NET, se riuscirò ad allocare il tempo.

Nel frattempo, se è accettabile avere cose tali da lavorare da R chiamando C#, cioè R è il punto di ingresso della tua applicazione, questo è sicuramente fattibile utilizzando il pacchetto . Ho colleghi che eseguono l'ottimizzazione e l'analisi MCMC di un modello scritto in C#. Uno tutorial è un caso molto semplice ma realistico di ottimizzazione da R utilizzando C#.

Problemi correlati