Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plots are not drawn with matplotlib #18

Open
sylee957 opened this issue Apr 7, 2023 · 3 comments
Open

Plots are not drawn with matplotlib #18

sylee957 opened this issue Apr 7, 2023 · 3 comments

Comments

@sylee957
Copy link
Member

sylee957 commented Apr 7, 2023

I see that the matplotlib is not recognized by the default backend in plots.

image

However, pyodide has matplotlib installed, so import matplotlib prepending just works

image

I wonder if this can be appended to the templates, or sympy plot should recognize this to load matplotlib on demand. (if loading matplotlib is heavy)

@asmeurer
Copy link
Member

asmeurer commented Apr 7, 2023

Apparently running import matplotlib installs matplotlib on the fly. But somehow this doesn't happen for the import matplotlib line that happens internally in the plotting module. Ideally any import that happens internally in SymPy should also install the module if it can, because those imports represent optional dependencies which enhance SymPy's capabilities.

NumPy is the same way. If you call lambdify, it will create a function that doesn't use the NumPy backend. But if you import numpy first it uses numpy:

Screen Shot 2023-04-07 at 1 57 47 PM

@asmeurer
Copy link
Member

Another thought: basically all external imports in SymPy go through this import_module() function. So we could potentially add some code in there to automatically import things if we detect we are in SymPy Live (this could either be added directly in SymPy or monkeypatched in SymPy Live).

Although I would like to understand why this is happening first. Maybe the problem is that pyodide intercepts import statements syntatically but import_module is using __import__. If that's the case, it would be better for pyodide to hook into Python's import mechanisms directly.

@agriyakhetarpal
Copy link
Collaborator

agriyakhetarpal commented Jan 18, 2025

Hi – I've taken a look at this, and I'd like to point out that Pyodide can't install packages by itself by intercepting import <..> statements; one has to go through the import micropip; await micropip.install("matplotlib") way of doing things so that Matplotlib can be present and can initialise itself before rendering plots. It is JupyterLite—on the other hand—which implements abstractions on top of micropip via piplite, and the kernel can use these import statements to install packages on the fly, but this is only possible if they are directly passed to the interpreter – and not within code for packages internally.

@oscarbenjamin and @ivanistheone, one solution for this issue could be to patch SymPy's import_module() function as suggested above, and the workaround is to import SymPy's optional dependencies before importing SymPy. I've experimented with a patch for the former today, so that the import_module() method functions just like %pip install, and I've got it to (partially) work:

JupyterLite REPL showing a modified form of SymPy's external module's import_module() function that can install

such that we can get import_module() to use piplite to install an optional dependency of SymPy on the fly if it's available, but only when running under JupyterLite/SymPy Live (while failing graciously if it's not available, of course). As it can be seen from the image, I currently have to call the import twice – once to load the wheel into its WASM form, and then to import it into the Python interpreter (I don't know why or how to fix this, yet). micropip's package installation is asynchronous, so we need to resort to tricks to establish synchronous boundaries over the installation by wrapping the piplite command in an event loop.

Another problem is around the mappings of the import names versus package names, as pip install python-flint does not correspond to import python-flint, for which we might need a custom dictionary of possible names for SymPy's optional dependencies.

Once fixed, I think it could be possible to fix and upstream the changes, but I'm also concerned about the maintainability of such a change. There's a good chance that a workaround of adding a %pip install matplotlib command beforehand, or, say, python-flint (as required in #23) and other packages can come at the cost of injecting too many packages, which, through eagerly loading everything can cause out of memory errors, so this approach ameliorates by making imports/installs lazy, at least.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants