-
-
Notifications
You must be signed in to change notification settings - Fork 331
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
WIP initial commit for issue 327: Add Swift binding #342
WIP initial commit for issue 327: Add Swift binding #342
Conversation
Thanks, @ScottThomasMiller, I will review it soon and will do some minor changes |
Per Andrey, I will move all global functions in BoardShim.swift to static functions within the type definition. |
I am working on the binding for perform_wavelet_transform in DataFilter.swift. The Java version returns a Pair type. The internet says a Java Pair is sort of like a Swift dictionary, with key/value pairs. But in your example code, you seem to use the return value like a normal tuple. Should my binding return a plain vanilla tuple, or a dictionary? |
Its a tuple of two arrays |
For perform_fft, which returns Complex[], I can think of three options off the top of my head:
Option 1 will take some time. Option 2 will introduce a dependency on a package that is not included in the standard Swift library. Option 3 forces the caller to do their own numeric processing of complex numbers. |
How does the process of installing new packages(dependencies) look like in Swift? If it's smth like in python or in java(declare them in files like setup.py or pom.xml and they are downloaded automatically) its ok to introduce Swift Numerics as a dependency. |
I think we need to add smth like this https://developer.apple.com/documentation/xcode/creating_a_standalone_swift_package_with_xcode and declare it as a dependency in project file |
It's pretty easy. In my Xcode project for my iOS app, I selected File-> Swift Packages->Add package dependency. And then I entered the Github URL for the Swift Numerics package: https://github.com/apple/swift-numerics.git. And then I followed the prompts to assign various dependencies. Xcode downloaded the package and set the build dependencies per the prompts. And then, at the top of DataFilter.swift I added:
Now I can declare the FFT function as returning an array of Complex Doubles, i.e.:
I will look into the package bundling, per the link you sent. |
Correction: I have to import the Numerics package itself, not ComplexModule. Otherwise it seems to be working fine. |
Its fine. We need to create brainflow package like its described at the link I've shared and add it as a dependency |
DataFilter.swift is 100% coded but only about 10% tested. I need help with its unit tests, specifically with designing a small handful of hardcoded input data arrays for which we know the expected output. I am working on the package. It's not easy. It won't build without the BrainFlow libraries. I think I need to create a framework for the C++ libs, and then include that framework as a dependency in the package. |
<< DataFilter.swift is 100% coded but only about 10% tested. I need help with its unit tests, specifically with designing a small handful of hardcoded input data arrays for which we know the expected output. You should take a look at tests for other languages, you can find them here https://github.com/brainflow-dev/brainflow/tree/master/tests/python for example. Also, need to add it to CI pipelines https://github.com/brainflow-dev/brainflow/blob/master/.github/workflows/run_unix.yml << I think I need to create a framework for the C++ libs, and then include that framework as a dependency in the package. No, you need to reuse CMake pipelines as for other languages and copy libs to a correct folder for swift. These libs should be packages together with swift binding |
About copying libs, there are multiple files like build.cmake in different folders and there are lines like this one https://github.com/brainflow-dev/brainflow/blob/master/src/board_controller/build.cmake#L168 it should be added for swift. Easiest option to find them all is |
In band_power_all.py, what are the expected values for band[0] and band[1]? |
If you use all eeg channels from synthetic board these values should be low |
There's a bug in DataFilter.getAvgBandPowers(). It's returning empty arrays. I'm looking into it. |
The bug was in my unit test, not in DataFilter. I updated my unit test, so that now, at the end of it, it does the following:
I ran the test successfully, 10 times in a row. |
Good, thanks! Let's also add 3rd main component(MLModel) and I will help to update the packaging |
Will do, as soon as I finish testing DataFilter. |
In https://github.com/brainflow-dev/brainflow/blob/master/tests/python/transforms.py, the following call to perform_fft() seems to be missing start/stop or length parameters:
|
No, for python it gets them from the shape of ndarray. It's done because of some language-specific implementations of arrays in java and numpy |
here is a comment about it. In c\c++ or in python you can use pointer offsets or slices, in java I didn't find anything like that and decided to add offsets as parameters |
I can rewrite DataFilter.performFFT() so that it is modeled after the Python, instead of the Java. Or I can overload it with both signatures. |
Its up to you |
I completed the initial phase of unit testing for DataFilter, using the Python tests as models. So far my unit tests cover only the following DataFilter methods: deTrend I will start including my unit test modules in the next commit. |
Is it unit tests or integration tests? We currently use tests as code samples at BrainFlow docs What you see here https://brainflow.readthedocs.io/en/stable/Examples.html#python is the same as what we run in CI pipelines to test code. And code sample on the docs page should be more like an integration test |
These are unit tests for Xcode. They test only that my bindings return data, and that the output data is different from the input data, except testBandPowerAll() which also compares averages. I am starting to include negative tests for specific exceptions. Per your suggestion I followed https://github.com/brainflow-dev/brainflow/tree/master/tests/python. From that page I implemented the following: testBandPower For the CI pipeline should I follow the Examples.html#python page instead? Is it possible to use one set of tests for both Xcode unit and Github CI? |
nice, feel free to push. Also, there were some breaking changes in brainflow 5.0.0, in MLModule predict now should return an array, in bandpass\bandstop now we use start and stop freqs and wavelets are enums now |
CI tests pushed, and merge conflicts resolved. I am looking at the 5.0.0 changes now. |
#441 I tried to write them in description for this pr |
How do I merge the 5.0.0 files with those in my branch? Like this? https://stackoverflow.com/questions/33484153/branch-is-behind-main |
in a folder where you cloned it run:
It will show you some conflicts, need to solve them and run:
|
Catching up to the latest version.
I'm working my way down the list. I am at this one:
I think we get that one for free with Swift. I can already declare smth like:
Do I need to do anything for this one? |
No, it was only for java and its speciffic to enums in java, in other languages they are integers from the box |
I see what I need to do for the last one, but do I need to worry about the next-to-last one:
|
Its also not relevant |
I finished coding the v5 upgrades and am almost finished testing them. My CI code throws an exception when trying to prepare an ML classifier, but only for specific combos of metric+classifier. The error is:
|
good, I will review it one more time and this time more carefully. I want to merge your changes before #510 because it will require changes in Swift binding also and if we merge this PR first I will port them to swift by myself. About metrics and classifiers, indeed not all combinations are supported, could you tell me which one you tried? Exception should be this one https://github.com/brainflow-dev/brainflow/blob/master/src/ml/ml_module.cpp#L67 and probably I need to add log message here. If you see an exception in json its a little strange |
This one works:
This one does not work:
|
its expected, onnx classifier and dyn lib classifiers can work only with USER_DEFINED metric. |
Correction: none of them are working. I was mislead by Xcode. Not even the ones that should work, for example mindfulness+default. |
it should work and tests in our languages are passed |
It was my bug. Fixed. Upgrades complete, including the CI tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job!
I wrote some comments but it looks really good.
One more question, files like DS_Store, what is it and do we really need them? I think they are pushed by mistake, at least they are in gitignore..
Svm model should also be removed.
About examples: I think we should split unittests and examples and make examples consistent with other languages
And the most important one: I dont see changes in CI jobs. You need to build swift package and run some tests here https://github.com/brainflow-dev/brainflow/blob/master/.github/workflows/run_unix.yml here is an example how to build smth only for macos https://github.com/brainflow-dev/brainflow/blob/master/.github/workflows/run_unix.yml#L54 here is an example how to run tests https://github.com/brainflow-dev/brainflow/blob/master/.github/workflows/run_unix.yml#L54
- in a shell such as iTerm2: | ||
|
||
cd /Users/myusername/brainflow/swift-package | ||
./build_package_macOS.sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think its better to add it to build.py and get rid off shell scripts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also build.py builds dylibs so it will decrease the number of steps
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will take a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the examples, do you want something like this:
>ls -l ./r_package/examples
total 80
-rw-r--r-- 1 scottmiller staff 957 Jun 24 06:17 band_power.R
-rw-r--r-- 1 scottmiller staff 706 Jun 24 06:17 band_power_all.R
-rw-r--r-- 1 scottmiller staff 356 Jun 24 06:17 brainflow_get_data.R
-rw-r--r-- 1 scottmiller staff 555 Jun 24 06:17 denoising.R
-rw-r--r-- 1 scottmiller staff 586 Jun 24 06:17 downsampling.R
-rw-r--r-- 1 scottmiller staff 1090 Jun 24 06:17 eeg_metrics.R
-rw-r--r-- 1 scottmiller staff 384 Jun 24 06:17 markers.R
-rw-r--r-- 1 scottmiller staff 541 Jun 24 06:17 serialization.R
-rw-r--r-- 1 scottmiller staff 708 Jun 24 06:17 signal_filtering.R
-rw-r--r-- 1 scottmiller staff 709 Jun 24 06:17 transforms.R
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, exactly. R is not the best example but you got the idea. Would be better to use python as a reference. You can also find them here https://github.com/brainflow-dev/brainflow/blob/master/docs/Examples.rst(will also need to add swift examples to this page)
- Copy the BrainFlow folder and its entire contents from /Users/myusername/brainflow/swift-package to /Users/myusername/myproject. | ||
- In Xcode select the top-level project and then click File->Add Packages...Add Local... | ||
- Select /Users/myusername/myproject/BrainFlow. Click Open. | ||
- Drag-and-drop the BrainFlowBindings subfolder from myproject/Packages/BrainFLow/Sources into myproject/myapp. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets create a github actions artifacts with all precompiled libraries, example can be found here https://github.com/brainflow-dev/brainflow/actions/runs/2572045283 https://github.com/brainflow-dev/brainflow/blob/master/.github/workflows/deploy_cpp_libs.yml#L104
We can trigger all required scripts to build it(build_package_macos.sh that should be moved to build.py) in CI and upload binaries, so other people will not even need to run it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe instead github artifacts we can publish to aws(run_unix workflow does it currently) I can take care of publishing and packaging but need to build package inside CI first
Swift | ||
------ | ||
|
||
.. literalinclude:: ../swift-package/BrainFlow/Tests/BrainFlowTests/BrainFlowCITests.swift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we separate them to match the structure with other languages? all of them have exactly the same code samples and they are separated. also lets add it before notebooks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
// Created by Scott Miller on 4/9/22. | ||
// | ||
|
||
#ifndef BrainFlow_h |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets replace by pragma once, its used in all other headers and lets make it consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
@@ -0,0 +1,16 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this file be in the repo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no
@@ -0,0 +1,7 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and this one
@@ -0,0 +1,8 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and this one and a few more files below
@@ -0,0 +1,20 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think its better to move it to build.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
@@ -0,0 +1,11 @@ | |||
// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not 100% sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe.
@@ -0,0 +1,7 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add it to CI as commands in yaml, dont see the need for shell script
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi. I took a break to work on trying to build the BrainFlow dylibs using Xcode so that they are acceptable to the App Store. I have made progress but I'm not quite there yet. I will return to focusing on the CI cmds in a few days. Later I think I should open a new issue for the App Store build.
Is it easy to build static .a libraries for BrainFlow, instead of .dylibs?
also, probably I missed it but I didnt see methods like |
I seem to have a lot of accidentally pushed flotsam in my branch. I apologize and will clean it all up shortly. |
Everything under swift-package/BrainFlowCI)/BrainFlowCI.xcodeproj/ was created by Xcode and I assume should be in the repo. |
I thought some of shell scripts\commands generate it or xcode creates it automatically, in this case probably we dont need it in the repo |
I had to merge one more change which will require swift package update. Here you can find more info https://brainflow.org/2022-07-15-brainflow-5-1-0/ and #510 CI is especially important now because I relocated and have no access to macos anymore... so its the only way for me to see that it actually works and passes tests |
This is my first-ever contribution to an open source project! Thanks to Andrey for his guidance and for inviting me to contribute these Swift bindings for BrainFlow. And thanks to William @ OpenBCI for his timely chime-ins on the BrainFlow Slack channel.
BoardShim.swift is fully coded and unit-tested using the synthetic board. DataFilter.swift is only about 25% complete, but I figured it's OK to include it in this WIP PR. Both modules are modeled after their Java counterparts.
Other to-do's include: