Skip to content

Matlab installation + adding Amita's code to wrapper #103

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

Merged
merged 18 commits into from
Jun 14, 2025

Conversation

oliverchampion
Copy link
Collaborator

Unit test will fail. Mainly checking whether matlab is called

Describe the changes you have made in this PR

Matlab installation on github
Adding Amita's code to wrapper

Link this PR to an issue [optional]

none

Checklist

  • [ X ] Self-review of changed code
  • [ X ] Added automated tests where applicable
  • Update Docs & Guides

Unit test will fail. Mainly checking whether matlab is called
- fixed initial guesses and bounds such that it passes tests
- changed matlab huild for github, hopefully overcoming errors.
Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

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

Sorry, not much time this week but took a quick look and had some thoughts.

@@ -30,6 +30,24 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Check out repository
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a checkout that happens about 10 lines above. Do you want 2?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed :)

- name: Set environment variables for MATLAB Engine
if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }}
run: |
if [[ "${{ runner.os }}" == "macOS" ]]; then
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see this variable set in the examples. What is it for? And does windows not need it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So, Matlab is saved at different locations depending on the OS. The matlab engine only points to the default windows location. So I need to add the matlab location to the path library for mac and linux. The first If statement is a bit obsolete, but the Windows simulator crashed on the second set of if statements, despite not needing to do anything there...

@@ -6,6 +6,9 @@
import time
from src.wrappers.OsipiBase import OsipiBase
#run using python -m pytest from the root folder
import matlab.engine
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be in the wrapper and not the test? Also the starting below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So this is the only way I can start Matlab engine ONCE without saving the algorithms. So now I give the wrapper the option to use a pre-defined matlab engine, OR if not defined, start the engine itself. By having the engine start in testing and be re-used per algorithm, it saves 20 seconds each time the algorithm is initiated (which is about 40 times or so).

Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

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

Despite my comments I think it's looking really good. I think this should handle matlab well.

supported_initial_guess = True
supported_thresholds = False

def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None,eng=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing a space before eng

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed



def __exit__(self, exc_type, exc_val, exc_tb):
if not self.keep_alive:
Copy link
Contributor

Choose a reason for hiding this comment

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

This is duplicated with the above so perhaps just a separate internal method that's called from both?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

@@ -5,8 +5,10 @@
import pathlib
import time
from src.wrappers.OsipiBase import OsipiBase
from conftest import eng
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is encouraged. https://stackoverflow.com/questions/68738078/is-it-discouraged-to-import-conftest-py-within-test-modules

I think the approved way would be to have a session scoped fixture so it would automatically be run once for each test session. @pytest.fixture(scope="session")

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OKay, so this is now "fixed". I did also move a lot of initialisation code from the tests to contest. Not sure whether that is completely where it should go, but it was in lign with other code that was there.

conftest.py Outdated
@@ -163,6 +178,11 @@ def algorithm_list(filename, selected, dropped):
with algorithm_path.open() as f:
algorithm_information = json.load(f)
algorithms = set(algorithm_information["algorithms"])
for algorithm in algorithms:
algorithm_dict = algorithm_information.get(algorithm, {})
if algorithm_dict.get("requieres_matlab", {}) == True:
Copy link
Contributor

Choose a reason for hiding this comment

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

You've misspelled requires, though you've been consistent so it'll work as intended!

Also here though you get the value of requires_matlab, which is True or False, but the default return is an empty dict {}. Shouldn't that be False?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed

conftest.py Outdated
for algorithm in algorithms:
algorithm_dict = algorithm_information.get(algorithm, {})
if algorithm_dict.get("requieres_matlab", {}) == True:
if eng is None:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really what we want to do, silently drop an algorithm if we don't have a running matlab engine? Would it be possible to mark all the tests with that algorithm as skipped instead? That way they'd be visible but not run.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've fixed this. Now they are skipped.

conftest.py Outdated
eng = None

def pytest_configure(config):
global eng
Copy link
Contributor

Choose a reason for hiding this comment

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

I have another comment related to this elsewhere, but global variables aren't generally encouraged. I could easily see this clashing in some algorithm that happens to use a variable called eng.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed

- Moved all the initiation functions from the unit_test folder to conftest.py
- Added a skip option for all matlab tests (instead of removing them)
- Removed some typos
- Removed "redundant" code
Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

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

Just a few small tweaks. Looks good though!

conftest.py Outdated
tolerances = algorithm_dict.get("tolerances", {})
skiptime=False
if first == True:
if algorithm_dict.get("fail_first_time", {}) == True:
Copy link
Contributor

Choose a reason for hiding this comment

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

Here's another case of comparing an empty dict to True

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed

conftest.py Outdated
if algorithm_dict.get("fail_first_time", {}) == True:
skiptime = True
first = False
if algorithm_dict.get("requires_matlab", False) == True:
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to == True if it's True already then that's all you want.
requires_matlab = algorithm_dict.get("requires_matlab", False)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, similar as previous one. Found this in several locations. Fixed it now

conftest.py Outdated
if len(selected) > 0 and selected[0]:
algorithms = algorithms & set(selected)
return list(algorithms)
if algorithm_dict.get("requires_matlab", False) == True:
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for == True or the if here as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed at multiple location

conftest.py Outdated
"strict": algorithm_dict.get("xfail_names", {}).get(name, True)}
kwargs = algorithm_dict.get("options", {})
tolerances = algorithm_dict.get("tolerances", {})
if algorithm_dict.get("requires_matlab", False) == True:
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly == True and no if here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

if requires_matlab:
max_time = 2
if eng is None:
print('test is here')
Copy link
Contributor

Choose a reason for hiding this comment

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

Print still needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

print('test is here')
pytest.skip(reason="Running without matlab; if Matlab is available please run pytest --withmatlab")
else:
print('test is not here')
Copy link
Contributor

Choose a reason for hiding this comment

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

And print here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

# assert save_file == "test"
ivim_algorithm, requires_matlab = algorithmlist
if requires_matlab:
Copy link
Contributor

Choose a reason for hiding this comment

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

Can be simplified to if requires_matlab and eng is None:

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

thanks!

Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

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

Nice, looks good!

@etpeterson etpeterson merged commit da7e8c9 into main Jun 14, 2025
29 checks passed
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

Successfully merging this pull request may close these issues.

2 participants