Выполнение кода в ядре v.5.2 из сеанса v.7.01 через MathLink - PullRequest
6 голосов
/ 13 февраля 2011

У меня Mathematica 7.01 и Mathematica 5.2 установлены на одной машине. Я хотел бы иметь возможность оценивать код в ядре v.5.2 из сеанса Mathematica 7.01. Я имею в виду, что при запуске Mathematica 7.0.1 стандартного сеанса мне нужна команда типа kernel5Evaluate для оценки некоторого кода в ядре 5.2 и возврата результата в ядро ​​7.01 и связанный блокнот 7.01 FrontEnd в таком виде так как этот код будет выполняться в ядре 7.01.

Например (в стандартной Mathematica v.7.01 сессия):

In[1]:= solutionFrom5 = kernel5Evaluate[NDSolve[{(y^\[Prime])[x]==y[x],y[1]==2},y,{x,0,3}]]
Out[1]= {{y -> InterpolatingFunction[{{0., 3.}}, <>]}}

In[2]:= kernel5Evaluate[Plot3D[Sin[x y],{x,-Pi,Pi},{y,-Pi,Pi}]]
During evaluation of In[2]:= GraphicsData["PostScript", "\<\............
Out[2]= -SurfaceGraphics-

В обоих случаях результат должен быть таким, как если бы ядро ​​v.5.2 было установлено как «Ядро ноутбука» во FrontEnd v.7.01. И, конечно, переменная solutionFrom5 должна быть установлена ​​на реальное решение, возвращаемое ядром v.5.2.

Ответы [ 3 ]

4 голосов
/ 14 февраля 2011

Вот реализация, основанная на коде Саймона.Это все еще требует улучшения.Одна неясная вещь для меня - как обрабатывать сообщения, генерируемые в подчиненном (v.5.2) ядре.

Вот мой код:

Clear[linkEvaluate]
SetAttributes[linkEvaluate, HoldRest]
linkEvaluate[link_LinkObject, expr_] := Catch[
   Module[{out = {}, postScript = {}, packet, outputs = {}},
    While[LinkReadyQ[link], 
     Print["From the buffer:\t", LinkRead[link]]];
    LinkWrite[link, Unevaluated[EnterExpressionPacket[expr]]];
    While[Not@MatchQ[packet = LinkRead[link], InputNamePacket[_]],
     Switch[packet,
      DisplayPacket[_], AppendTo[postScript, First@packet],
      DisplayEndPacket[_], AppendTo[postScript, First@packet]; 
      CellPrint@
         Cell[GraphicsData["PostScript", #], "Output", 
          CellLabel -> "Kernel 5.2 PostScript ="] &@
       StringJoin[postScript]; postScript = {},
      TextPacket[_], 
      If[StringMatchQ[First@packet, 
        WordCharacter .. ~~ "::" ~~ WordCharacter .. ~~ ": " ~~ __], 
       CellPrint@
        Cell[BoxData@
          RowBox[{StyleBox["Kernel 5.2 Message = ", 
             FontColor -> Blue], First@packet}], "Message"], 
       CellPrint@
        Cell[First@packet, "Output", CellLabel -> "Kernel 5.2 Print"]],
      OutputNamePacket[_], AppendTo[outputs, First@packet];,
      ReturnExpressionPacket[_], AppendTo[outputs, First@packet];,
      _, AppendTo[out, packet]
      ]
     ];
    If[Length[out] > 0, Print[out]];
    Which[
     (l = Length[outputs]) == 0, Null,
     l == 2, Last@outputs,
     True, multipleOutput[outputs]
     ]
    ]];
Clear[kernel5Evaluate]
SetAttributes[kernel5Evaluate, HoldAll]
kernel5Evaluate[expr_] := 
 If[TrueQ[MemberQ[Links[], $kern5]], linkEvaluate[$kern5, expr], 
  Clear[$kern5]; $kern5 = LinkLaunch[
    "C:\\Program Files\\Wolfram Research\\Mathematica\\5.2\\MathKernel.exe -mathlink"]; 
  LinkRead[$kern5]; 
  LinkWrite[$kern5, 
   Unevaluated[EnterExpressionPacket[$MessagePrePrint = InputForm;]]];
   LinkRead[$kern5]; kernel5Evaluate[expr]]

Вот тестовые выражения:

plot = kernel5Evaluate[Plot3D[Sin[x y], {x, 0, Pi}, {y, 0, Pi}]]
plot = kernel5Evaluate[Plot[Sin[x], {x, 0, Pi}]; Plot[Sin[x], {x, -Pi, Pi}]] // 
  DeleteCases[#, HoldPattern[DefaultFont :> $DefaultFont], Infinity] &
s = kernel5Evaluate[
  NDSolve[{y'[x] == y[x] Cos[x + y[x]], y[0] == 1}, y, {x, 0, 30}]]
s // InputForm // Short
kernel5Evaluate[1/0; Print["s"];]

Кажется, работает как ожидалось.Однако это может быть лучше ...

2 голосов
/ 16 февраля 2011

Вот рабочая реализация того, что я хотел.Я добавил проверку отсутствия соединения MathLink, как предложил Тодд Гейли here.Теперь kernel5Evaluate работает надежно, даже если подчиненное ядро ​​было прервано необычным способом.Я также значительно улучшил разбор Message с и добавил несколько диагностических сообщений для kernel5Evaluate.Вот код:

$kern5Path = "C:\\Program Files\\Wolfram Research\\Mathematica\\5.2\\MathKernel.exe";

Clear[printMessage, printPrint, printPostScript]
printMessage[str_String] := 
  CellPrint@
   Cell[BoxData[
     RowBox[StringSplit[str, 
        x : ("MyDelimeterStart" | "MyDelimeterEnd") :> x] //. {x___, 
         "MyDelimeterStart", y_, "MyDelimeterEnd", z___} :> {x, 
         ToExpression[y], z}]], "Message", 
    CellLabel -> "(Kernel 5.2)", ShowCellLabel -> True];
printPostScript = 
  CellPrint@
    Cell[GraphicsData["PostScript", #], "Graphics", 
     CellLabel -> "(Kernel 5.2 PostScript)", ShowCellLabel -> True] &;
printPrint[str_String] := 
  CellPrint@
   Cell[If[StringTake[str, -1] === "\n", StringDrop[str, -1], str], 
    "Print", CellLabel -> "(Kernel 5.2 print, text mode)", 
    ShowCellLabel -> True];

Clear[linkEvaluate]
SetAttributes[linkEvaluate, HoldAllComplete]
linkEvaluate[link_LinkObject, expr_] := Catch[
   Module[{out = {}, postScript = {}, packet, result = Null},
    If[LinkReadyQ[link], 
     While[LinkReadyQ[link], 
      Print["Rest of the buffer:\t", 
       packet = LinkRead[link, Hold]]];
     If[Not@MatchQ[packet, Hold[InputNamePacket[_]]], 
      Message[kernel5Evaluate::linkIsBusy]; Throw[$Failed]]];
    LinkWrite[link, Unevaluated[EnterExpressionPacket[expr]]];
    While[
     Check[Not@
       MatchQ[packet = LinkRead[link, Hold], 
        Hold[InputNamePacket[_]]], 
      Message[kernel5Evaluate::linkIsClosed]; Throw[$Failed]],
     Switch[packet,
      Hold@DisplayPacket[_String], 
      AppendTo[postScript, First@First@packet],
      Hold@DisplayEndPacket[_String], 
      AppendTo[postScript, First@First@packet]; 
      printPostScript@StringJoin[postScript]; postScript = {},
      Hold@MessagePacket[__], ,
      Hold@TextPacket[_String], 
      If[StringMatchQ[First@First@packet, 
        WordCharacter .. ~~ "::" ~~ WordCharacter .. ~~ ": " ~~ __], 
       printMessage[First@First@packet], 
       printPrint[First@First@packet]],
      Hold@OutputNamePacket[_], ,
      Hold@ReturnExpressionPacket[_], result = First[First[packet]],
      _, AppendTo[out, packet]
      ]
     ];
    If[Length[out] > 0, Print["Unparsed packets: ", out]];
    result
    ]];
Clear[kernel5Evaluate]
SetAttributes[kernel5Evaluate, HoldAllComplete]
kernel5Evaluate::usage = "kernel5Evaluate[\!\(\*
StyleBox[\"expr\",\nFontFamily->\"Times New Roman\",\n\
FontSlant->\"Italic\"]\)] writes \!\(\*
StyleBox[\"expr\",\nFontFamily->\"Times New Roman\",\n\
FontSlant->\"Italic\"]\) to MathKernel 5.2, returns output and prints \
messages generated during computation.";
kernel5Evaluate::linkIsBusy = 
  "Kernel 5.2 is still running previous calculation.";
kernel5Evaluate::linkIsClosed = "Connection to Kernel 5.2 is lost.";
kernel5Evaluate::kernel5NotFound = 
  "Path `1` not found. Please set variable $kern5Path to correct path \
to MathKernel 5.2.";
kernel5Evaluate[expr_] :=
 If[TrueQ[MemberQ[Links[], $kern5]],
  If[LinkReadyQ[$kern5]; First[LinkError[$kern5]] == 0, 
   With[{$kern5 = $kern5}, linkEvaluate[$kern5, expr]], 
   LinkClose[$kern5]; kernel5Evaluate[expr]],
  Clear[$kern5];
  If[FileExistsQ[$kern5Path],
   $kern5 = LinkLaunch[$kern5Path <> " -mathlink -noinit"]; 
   LinkRead[$kern5]; LinkWrite[$kern5,
    Unevaluated[
     EnterExpressionPacket[$MessagePrePrint = ("MyDelimeterStart" <> 
          ToString[ToBoxes[#]] <> "MyDelimeterEnd") &; 
      SetOptions[$Output, {PageWidth -> Infinity}];]]]; 
   LinkRead[$kern5]; kernel5Evaluate[expr], 
   Message[kernel5Evaluate::kernel5NotFound, $kern5Path]; $Failed]
  ]

А вот несколько тестовых выражений:

kernel5Evaluate[Unevaluated[2 + 2]]
kernel5Evaluate[$Version]
kernel5Evaluate[Quit[]]
kernel5Evaluate[Print["some string"];]
kernel5Evaluate[Sin[1,]]
kernel5Evaluate[1/0]

kernel5Evaluate[{Plot[Sin[x], {x, 0, Pi}], 
   Plot[Sin[x], {x, -Pi, Pi}]}] // 
 DeleteCases[#, HoldPattern[DefaultFont :> $DefaultFont], Infinity] &

kernel5Evaluate[Plot3D[Sin[x y], {x, 0, Pi}, {y, 0, Pi}]];
ListPlot3D[First@%, Mesh -> Full, DataRange -> MeshRange /. Last[%]]

s = kernel5Evaluate[
  NDSolve[{y'[x] == y[x] Cos[x + y[x]], y[0] == 1}, y, {x, 0, 30}]]
% // InputForm // Short

kernel5Evaluate[ContourPlot[Sin[x y], {x, -5, 5}, {y, -5, 5}]];
ListContourPlot[First@%, DataRange -> MeshRange /. Last[%], 
 Contours -> 10, 
 Method -> {"Refinement" -> {"CellDecomposition" -> "Quad"}}]
2 голосов
/ 13 февраля 2011

Вот моя попытка того, что вы хотите,

Сначала я определяю linkEvaluate, который принимает активный Link и передает ему выражение.Если есть вещи для LinkRead, которые еще нужно прочитать, то они читают их, пока их больше нет.Затем он пишет выражение и ждет результатов, чтобы вернуться.Затем он читает вывод, пока не останется ничего для чтения.Обычно он возвращает первое ReturnExpressionPacket, если вы не установили последний, необязательный аргумент all равным True - в этом случае он возвращает все прочитанное.* сначала проверяет, определен ли глобальный $kern5 как LinkObject, если нет, то определяет его.Затем он просто передает работу на linkEvaluate.Вам придется заменить «math5» на имя файла и путь вашего ядра Mma 5.2.

Clear[kernel5Evaluate]
SetAttributes[kernel5Evaluate, HoldAll]
kernel5Evaluate[expr_, all:(True|False):False] := If[TrueQ[MemberQ[Links[], $kern5]], 
  linkEvaluate[$kern5, expr, all], 
  Clear[$kern5]; $kern5 = LinkLaunch["math5 -mathlink"]; kernel5Evaluate[expr,all]
  ]
...