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

Add support for Elixir #447

Merged
merged 8 commits into from
Feb 19, 2022
Merged

Conversation

lucasavila00
Copy link
Contributor

No description provided.

* Add support for Elixir

* try to fix CI

* try to fix CI

* try to fix CI

* try to fix CI

* revert changes on CI file

* revert fomatting changes

* enable all tests

* update readme

* revert change to CI file

* fix api test

* fix init.py fmt
@lucasavila00
Copy link
Contributor Author

I see codecov is failing, I guess it's because I haven't added tests to the interpreter folder, only e2e.

I'll add the missing tests.

@lucasavila00
Copy link
Contributor Author

For some context on the decisions made in the Elixir Interpreter (ie: why isn't it returning a list? why does it return a binary?):

Copy link
Member

@StrikerRUS StrikerRUS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucasavila00 Thank you so so much! This is amazing! I gave this PR a brief look and can say it is high quality. Unfortunately, I'm not familiar with Elixir, so I need some time to learn the basics and read the links you've provided above in order to give a thoughtful review. Given that you've already written end-to-end tests for Elixir and they are passed, I believe there might be only minor requests for changes.
However, I've left some initial questions I can ask knowing nothing about Elixir below.

I see codecov is failing, ...

Please ignore this for now. I'll help to deal with all formal tests, linting errors, supporting code and so on.

Also, I'll be very grateful if you could write one or two sentences disclosing the story behind this PR: how did you find m2cgen and why Elixir 🙂 .

m2cgen/cli.py Show resolved Hide resolved
m2cgen/interpreters/elixir/code_generator.py Show resolved Hide resolved
m2cgen/interpreters/elixir/interpreter.py Show resolved Hide resolved
tests/e2e/test_e2e.py Show resolved Hide resolved
@lucasavila00
Copy link
Contributor Author

Thanks for the kind words: )

Also, I'll be very grateful if you could write one or two sentences disclosing the story behind this PR: how did you find m2cgen and why Elixir slightly_smiling_face .

I have been using m2cgen on my job for more than a year. We use it to convert models to JS, mostly. I'm a super fan of m2cgen. I had read the source code of m2cgen a bit, and I'm super comfortable analyzing the generated files.

I have been learning elixir for the past months, and I missed having m2cgen available for Elixir.

Elixir is a great language for hosting m2cgen compiled modules, as the language/vm have native support for loading new models while the old ones are executing, for instance. ( https://blog.appsignal.com/2018/10/16/elixir-alchemy-hot-code-reloading-in-elixir.html https://blog.appsignal.com/2021/07/27/a-guide-to-hot-code-reloading-in-elixir.html )

Also, the language/vm parallelism would work really well with m2cgen generated models, allowing it to scale basically for free.

@StrikerRUS
Copy link
Member

I have been using m2cgen on my job for more than a year.

Wow, impressive! Thanks a lot for sharing your experience.

I'm sorry for not getting back to you earlier, but I was reading official Elixir guide to get a little bit familiar with the language and its opportunities. Now I'm done and will provide a thoughtful review for this PR very soon.

@StrikerRUS
Copy link
Member

Sorry for the bustle here 😬 - just wanted to be sure this PR is playing well with recently updated major versions of the dependencies.

@lucasavila00
Copy link
Contributor Author

Sure, I'll do it all.
I'm sorry for taking this long, I have been on a rush lately but I haven't forgotten about this project.

@StrikerRUS
Copy link
Member

Kindly ping @lucasavila00 🙂

@lucasavila00
Copy link
Contributor Author

@StrikerRUS I'm sorry. I plan to have it done this weekend. If I'm unable to do it I close the MR.

@StrikerRUS
Copy link
Member

I plan to have it done this weekend.

Super!

If I'm unable to do it I close the MR.

Please don't do it! I strongly believe your contribution is extremely valuable. As you can see, there is no rush at all 😉 . If you can do it next weekend or even some time later, that's fine. Also, if you have no time at all to finish this PR, that's totally OK as well. I think I can take it over (push some changes directly into your fork or done them via a follow-up PRs) if you don't mind.

* Receive and return lists

* fix lint issue

* fix api tests
@lucasavila00
Copy link
Contributor Author

@StrikerRUS aside from the issue of NaN not being supported by Erlang VM, I think the code is ready.

But there's a flaky test and I'm not sure what to do (failed 1/5 times):

tests/e2e/test_e2e.py::test_e2e[xgboost_XGBClassifier - elixir - train_model_classification_binary2]

E           RuntimeError: Bad exit code (1), stderr:
E           ** (ArithmeticError) bad argument in arithmetic expression
E               (stdlib 3.15.2) :math.exp(143625999918830.8)
E               /tmp/tmp_swiqff8/score.ex:27: Model.sigmoid/1
E               /tmp/tmp_swiqff8/score.ex:24: Model.score/1
E               /tmp/tmp_swiqff8/score.ex:36: Runner.run/0
E               (stdlib 3.15.2) erl_eval.erl:685: :erl_eval.do_apply/6
E               (elixir 1.12.0) lib/code.ex:656: Code.eval_string_with_error_handling/3

In the end :math.exp(143625999918830.8) is trying to calculate a float that's bigger than erlang can represent: https://stackoverflow.com/a/38533723

@@ -43,6 +49,7 @@ def add_function_def(self, name, args, is_scalar_output):
self.add_code_line(function_def)

self.increase_indent()
self.add_code_line("input = list_to_binary(input)")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a less hacky way to do this?

The name of the overridden method add_function_def suggests this is a generic function definition, not the "score" function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late response - was busy these days. TBH, I don't want to delay this PR and bother you with any other investigations or significant code refactoring. I think this approach is OK for now. Later I can refactor this.

Only one thing bothers me,

But there's a flaky test and I'm not sure what to do (failed 1/5 times):

do you mean that right now that test can fail any time? It's quite odd because I believe all random seeds are fixed in tests... BTW, I remember we had another problem with this particular test: #205. If so, let's just skip that test for Elixir for now similarly to how tests with missing values (NaNs) are being skipped ring now. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean that right now that test can fail any time? It's quite odd because I believe all random seeds are fixed in tests...

yeah, it happened a few times randomly - my perception is that it happens 20% of the time

If so, let's just skip that test for Elixir for now similarly to how tests with missing values (NaNs) are being skipped ring now. WDYT?

I tried to do this, but I couldn't figure out how to skip a specific test in the test suite. Would you mind pointing me how to do it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can simply deselect that test for now here

pytest -v "-m=$LANG" tests/e2e/

via -k option:

pytest -v "-m=$LANG" "-k=not(xgboost_XGBClassifier and elixir and train_model_classification_binary2)" tests/e2e/

lucasavila00 and others added 2 commits January 16, 2022 14:41
@StrikerRUS
Copy link
Member

@lucasavila00 Thank you so much for this contribution again! 🚀

I'm going to merge this PR and after doing that make some small improvements in a follow-up PRs. If there would a need in high-level expertise in Elixir I'll @ ping you, if you don't mind 🙂 .

@lucasavila00
Copy link
Contributor Author

I'm glad this was merged. I'm sorry for missing the message on the -k option.

@StrikerRUS StrikerRUS merged commit 9c7e0f4 into BayesWitnesses:master Feb 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants