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

append all Python class bases to R class attribute #26

Closed
goldingn opened this issue Mar 21, 2017 · 4 comments
Closed

append all Python class bases to R class attribute #26

goldingn opened this issue Mar 21, 2017 · 4 comments

Comments

@goldingn
Copy link
Contributor

goldingn commented Mar 21, 2017

When executing a python function with reticulate, class information from the python object is appended to the resulting R object's class attribute. This is done in the CPP function py_ref.

If I understand this code correctly, py_ref grabs the __class__ attribute of the python object (the class of which this is an instance), as well as the parent class from which this class inherits, via the class object's .__bases__ attribute. However this is only done for the immediate parent class, not for the full inheritance.


I am working on a port of the GPflow Python module, and want to map its nice kernel creation syntax to R. Kernel objects can be added, multiplied etc., and the GPflow module enables this by defining addition for all objects of class Kern. for example, in python:

import GPflow as gp
k1 = gp.kernels.Constant(1)
k2 = gp.kernels.Bias(1)
k3 = gp.kernels.Cosine(1)
K = k1 + k2 + k3

The full class inheritance for these objects is:
k1: object>Parentable>Parameterized>Kern>Static>Constant
k2: object>Parentable>Parameterized>Kern>Static>Constant>Bias
k3: object>Parentable>Parameterized>Kern>Stationary>Cosine
The + operator is a wrapper around GPflow.kernels.Add((k1, k2, k3)), defined in the Kern class; it works for any class instance inheriting from Kern.

To enable the same syntax in an R port using reticulate, it would be really helpful for GPflow.kernels.Kern to be listed in the class attribute for the corresponding R objects, so we can do S3 dispatch. But because only the immediate parent class is returned this isn't currently possible. I.e. in R:

library(reticulate)
gp <- import('GPflow')
k1 <- gp$kernels$Constant(1)
k2 <- gp$kernels$Bias(1)
k3 <- gp$kernels$Cosine(1)
class(k1); class(k2); class(k3)
[1] "GPflow.kernels.Constant" "GPflow.kernels.Static"   "python.builtin.object"  
[1] "GPflow.kernels.Bias"     "GPflow.kernels.Constant" "python.builtin.object"
[1] "GPflow.kernels.Cosine"     "GPflow.kernels.Stationary" "python.builtin.object" 

A fix would be to have this section of py_ref recurse through the parent classes, finding their parents (via the .__bases__ attribute) and adding them to the R object's class attribute, until hitting either object or an empty string (object's parent class).

As far as I can tell, there is no sane way of doing this from the reticulate-wrapping GPflow-specific R package.

If that sounds like behaviour you would like (or tolerate) in reticulate, I'd be happy to do a PR (though my C++ is decidedly rusty so very happy if someone else wants to do it!).

@jjallaire
Copy link
Member

Interesting, I was under the impression that __bases__ would provide the entire hierarchy. Looks like that logic needs to be in a loop up to the top level. I'll try to make that change for the next release.

@jjallaire
Copy link
Member

It looks like inspect.getmro will do the correct traversal: http://stackoverflow.com/questions/1401661/list-all-base-classes-in-a-hierarchy-of-given-class

@jjallaire
Copy link
Member

@goldingn This PR should address this issue: #31

Could you take a look and let me know if this looks okay to you? (i.e. am I missing any subtleties here)

@goldingn
Copy link
Contributor Author

Yeah, that's perfect. I get:

devtools::install_github('rstudio/reticulate@930b283')
library(reticulate)
gp <- import('GPflow')
k1 <- gp$kernels$Constant(1)
k2 <- gp$kernels$Bias(1)
k3 <- gp$kernels$Cosine(1)
class(k1); class(k2); class(k3)
[1] "GPflow.kernels.Constant"    "GPflow.kernels.Static"      "GPflow.kernels.Kern"       
[4] "GPflow.param.Parameterized" "GPflow.param.Parentable"    "python.builtin.object"     
[1] "GPflow.kernels.Bias"        "GPflow.kernels.Constant"    "GPflow.kernels.Static"     
[4] "GPflow.kernels.Kern"        "GPflow.param.Parameterized" "GPflow.param.Parentable"   
[7] "python.builtin.object"     
[1] "GPflow.kernels.Cosine"      "GPflow.kernels.Stationary"  "GPflow.kernels.Kern"       
[4] "GPflow.param.Parameterized" "GPflow.param.Parentable"    "python.builtin.object" 

Thanks!

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

2 participants