matlab_kernel icon indicating copy to clipboard operation
matlab_kernel copied to clipboard

flush plots before script finishes

Open kb1ooo opened this issue 4 years ago • 9 comments

Is it possible to push/flush inline plots to cell output while a script is running. E.g. if you were to make plots at every iteration of a loop, they won't render until the loop is completed. Is there a way (without doing native plots) to flush plots? This is pretty important for looking at progress of iterative optimization runs, e.g. deep learning training.

Thanks

kb1ooo avatar Mar 11 '20 19:03 kb1ooo

In theory, we could inject a matlab function that prints figures to a known location and then have a python thread that looks for changes in that directory, then triggers an image display on the kernel. You would then be able to call that function from within your loop. If someone were willing to take that effort up, I could offer pointers.

blink1073 avatar Mar 12 '20 00:03 blink1073

@blink1073 ok, interesting. Do you think it would be possible to push updates to the same plot as well? I might be willing to take this on depending on the effort involved.

kb1ooo avatar Mar 12 '20 11:03 kb1ooo

@blink1073 regarding your suggestion, would it not be possible to do something synchronous? As long as you are injecting a custom "plotting" matlab function, is there no way to invoke an image display directly after that call?

kb1ooo avatar Mar 12 '20 13:03 kb1ooo

@blink1073 what about the following (inspired by a great hack a web based Terminal program called wetty uses, which allows you to download files via writing to the terminal in between escape sequences). So, what if you inject a custom matlab function that prints the figure somewhere (like you suggested) and then writes the filename to stdout in between some escape sequences. Modify your _PseudoStream class to intercept the escape sequence, pull the filename out and call Display on the image. Otherwise, the _PseudoStream prints to the stream as it does now.

kb1ooo avatar Mar 12 '20 16:03 kb1ooo

I had a similar idea that I listed either here, or in the matlab_kernel's parent. I was always hoping that we could do it in some kind of "standard" way, so that it was as hacky as it could be :) Let me see if I can find some notes...

dsblank avatar Mar 12 '20 16:03 dsblank

@blink1073 @dsblank I made a first pass at this on my fork based on the approach I described above. Please check out my fork if you would like, and let me know what you think. I heavily commented the kernel code, but I'll repeat some of it here. I wrote a variation of the _PseudoStream class called _PseudoStreamFig which looks for start '\033[5i' and end '\033[4i' escape sequence delimiters in the stdout stream. Anything not between those delimiters it passes to the stdout writer just as the original _PseudoStream class did. In between the delimiters, it looks for a URI of the form fig://urlencoded_filename.png/gcf?id=12&rm=1, which communicates the figure filename, the gcf id (which I would like to use in the future to update figure if possible through the metakernel), and then rm=1 or rm=0 to signify whether the file should be removed after rendering.

So, to communicate that a fig should be rendered in jupyter you call a function from matlab which looks something like:

function jupFigRender(fig)

    if nargin == 0
        fig = gcf;
    end

    gcfid = num2str(get(fig,'Number'));
    dpi = 96;
    filename = [tempname '.png'];
    print(fig,'-dpng',sprintf('-r%i', dpi), filename);
    drawnow('update'); % flushes stdout buffer
    fprintf(1,'%s[5ifig://%s/gcf?id=%s&rm=1%s[4i',...
        27,urlencode(filename),gcfid,27)
    drawnow('update'); % flushes stdout buffer
end

If you have suggestions on how to elegantly make something like this function a no-op when you aren't running code from jupyter, that would be helpful. Also, I'd really like to update this to be able to assign a display id and then also use update_display from ipython. Is this possible through the metakernel interface?

kb1ooo avatar Apr 07 '20 02:04 kb1ooo

Looks good so far!

  • You could set a property on 0 during kernel startup and check for that property in the script.
  • We don't currently support update_display, but here's what you'd need to do:
    • Update Display to include transient
    • Add an UpdateDisplay method to that class that generates an UpdateDisplay message.

blink1073 avatar Apr 07 '20 09:04 blink1073

Looks good so far!

* You could [set](http://www.ece.northwestern.edu/local-apps/matlabhelp/techdoc/ref/set.html) a property on `0` during kernel startup and check for that property in the script.

Ok cool, so you mean add something like 'UserData': 'jupyter' to the default properties dictionary in handle_plot_settings?

* We don't currently support `update_display`, but here's what you'd need to do:
  
  * Update [`Display`](https://github.com/Calysto/metakernel/blob/master/metakernel/_metakernel.py#L625) to include [`transient`](https://jupyter-client.readthedocs.io/en/stable/messaging.html#display-data)
  * Add an `UpdateDisplay` method to that class that generates an `UpdateDisplay` [message](https://jupyter-client.readthedocs.io/en/stable/messaging.html#update-display-data).

Great, thanks.

kb1ooo avatar Apr 07 '20 18:04 kb1ooo

Ok cool, so you mean add something like 'UserData': 'jupyter' to the default properties dictionary in handle_plot_settings?

Yeah, that sounds right.

blink1073 avatar Apr 08 '20 08:04 blink1073