Running pyAFQ 2.x defaults in pyAFQ 3.x#
from AFQ.api.group import GroupAFQ
import AFQ.data.fetch as afd
import AFQ.definitions.image as afm
import AFQ.api.bundle_dict as abd
import os.path as op
afd.organize_stanford_data()
2026-05-19 01:01:04,307 INFO util.py:154 -- Missing packages: ['ipywidgets']. Run `pip install -U ipywidgets`, then restart the notebook server for rich notebook output.
Tractography parameters in the old way#
In pyAFQ 2.x, we used CSD with no asymmetric filtering, and seeded streamlines throughout the white matter instead of on the white matter / gray matter interface.
tracking_params = dict(
odf_model="csd",
n_seeds=1,
random_seeds=False,
minlen=50,
directions="prob",
seed_mask=afm.ScalarImage("dti_fa"),
seed_threshold=0.2
)
Partial Volume Estimate in the old way#
In pyAFQ 2.x, we did not use PVE and instead thresholded on fractional anisotropy (FA) maps to create seed and stopping masks. Here, we recreate the PVE images using the FA maps. Note there the CSF map is not used in this case. Additionally, in pyAFQ 2.x, the brain mask was calculated using median OTSU. Here, we import it from the Freesurfer segmentation instead.
pve = afm.PVEImages(
afm.ThresholdedScalarImage(
"dti_fa",
upper_bound=0.0),
afm.ThresholdedScalarImage(
"dti_fa",
upper_bound=0.2),
afm.ThresholdedScalarImage(
"dti_fa",
lower_bound=0.2))
bm_def = afm.LabelledImageFile(
suffix="seg", filters={"scope": "freesurfer"},
exclusive_labels=[0])
VOF / pAF / CST in the old way#
In pyAFQ 2.x, the vertical occipital fasciculus (VOF) and posterior arcuate fasciculus (pAF) were defined differently. The pAF in 3.0 has an increased restriction that it cannot overlap with the arcuate by more than 30%. The VOF has several changes:
one endpoint ROI instead of both, but there is a minimum length requirement to of 25mm to compensate;
The allowed overlap with the pAF has been reduced;
it must be lateral to the inferior fronto-occipital fasciculus instead of the inferior longitudinal fasciculus;
cleaning has been changed: there is now mahalanobis cleaning on orientation, and isolation forest cleaning instead of mahalanobis for distance. Additionally, in the new version, the inferior endpoints of the corticospinal tracts (CST) were removed, and the superior longitudinal fasciculus (SLF) was broken into three sub-bundles.
templates = afd.read_templates(as_img=False)
old_vof_paf_cst_slf_definitions = abd.BundleDict({
'Left Corticospinal': {
'cross_midline': False,
'include': [templates['CST_roi2_L'],
templates['CST_roi1_L']],
'exclude': [],
'space': 'template',
'prob_map': templates['CST_L_prob_map'],
'end': templates['CST_L_start'],
'start': templates['CST_L_end']},
'Right Corticospinal': {
'cross_midline': False,
'include': [templates['CST_roi2_R'],
templates['CST_roi1_R']],
'exclude': [],
'space': 'template',
'prob_map': templates['CST_R_prob_map'],
'end': templates['CST_R_start'],
'start': templates['CST_R_end']},
"Left Superior Longitudinal": {
"cross_midline": False,
"include": [templates["SLF_roi1_L"], templates["SLF_roi2_L"]],
"exclude": [templates["SLFt_roi2_L"]],
"space": "template",
"prob_map": templates["SLF_L_prob_map"],
"start": templates["SLF_L_start"],
"end": templates["SLF_L_end"]},
"Right Superior Longitudinal": {
"cross_midline": False,
"include": [templates["SLF_roi1_R"], templates["SLF_roi2_R"]],
"exclude": [templates["SLFt_roi2_R"]],
"space": "template",
"prob_map": templates["SLF_R_prob_map"],
"start": templates["SLF_R_start"],
"end": templates["SLF_R_end"]},
'Left Posterior Arcuate': {'cross_midline': False,
'include': [templates['SLFt_roi2_L']],
'exclude': [templates['SLF_roi1_L']],
'space': 'template',
'start': templates['pARC_L_start'],
'primary_axis': 'I/S',
'primary_axis_percentage': 40},
'Right Posterior Arcuate': {'cross_midline': False,
'include': [templates['SLFt_roi2_R']],
'exclude': [templates['SLF_roi1_R']],
'space': 'template',
'start': templates['pARC_R_start'],
'primary_axis': 'I/S',
'primary_axis_percentage': 40},
'Left Vertical Occipital': {'cross_midline': False,
'space': 'template',
'start': templates['VOF_L_start'],
'end': templates['VOF_L_end'],
'inc_addtol': [4, 0],
'Left Arcuate': {
'node_thresh': 20},
'Left Posterior Arcuate': {
'node_thresh': 1,
'core': 'Anterior'},
'Left Inferior Longitudinal': {
'core': 'Right'},
'primary_axis': 'I/S',
'primary_axis_percentage': 40},
'Right Vertical Occipital': {'cross_midline': False,
'space': 'template',
'start': templates['VOF_R_start'],
'end': templates['VOF_R_end'],
'inc_addtol': [4, 0],
'Right Arcuate': {
'node_thresh': 20},
'Right Posterior Arcuate': {
'node_thresh': 1,
'core': 'Anterior'},
'Right Inferior Longitudinal': {
'core': 'Left'},
'primary_axis': 'I/S',
'primary_axis_percentage': 40}})
Callosal bundles in the old way#
In pyAFQ 2.x, the callosal bundles were cleaned using mahalnobis instead of isolation forest.
callosal_templates =\
afd.read_callosum_templates(as_img=False)
callosal_bd = abd.BundleDict({
'Callosum Anterior Frontal': {
'cross_midline': True,
'include': [callosal_templates['R_AntFrontal'],
callosal_templates['Callosum_midsag'],
callosal_templates['L_AntFrontal']],
'exclude': [],
'space': 'template'},
'Callosum Motor': {
'cross_midline': True,
'include': [callosal_templates['R_Motor'],
callosal_templates['Callosum_midsag'],
callosal_templates['L_Motor']],
'exclude': [],
'space': 'template'},
'Callosum Occipital': {
'cross_midline': True,
'include': [callosal_templates['R_Occipital'],
callosal_templates['Callosum_midsag'],
callosal_templates['L_Occipital']],
'exclude': [],
'space': 'template'},
'Callosum Orbital': {
'cross_midline': True,
'include': [callosal_templates['R_Orbital'],
callosal_templates['Callosum_midsag'],
callosal_templates['L_Orbital']],
'exclude': [],
'space': 'template'},
'Callosum Posterior Parietal': {
'cross_midline': True,
'include': [callosal_templates['R_PostParietal'],
callosal_templates['Callosum_midsag'],
callosal_templates['L_PostParietal']],
'exclude': [],
'space': 'template'},
'Callosum Superior Frontal': {
'cross_midline': True,
'include': [callosal_templates['R_SupFrontal'],
callosal_templates['Callosum_midsag'],
callosal_templates['L_SupFrontal']],
'exclude': [],
'space': 'template'},
'Callosum Superior Parietal': {
'cross_midline': True,
'include': [callosal_templates['R_SupParietal'],
callosal_templates['Callosum_midsag'],
callosal_templates['L_SupParietal']],
'exclude': [],
'space': 'template'},
'Callosum Temporal': {
'cross_midline': True,
'include': [callosal_templates['R_Temporal'],
callosal_templates['Callosum_midsag'],
callosal_templates['L_Temporal']],
'exclude': [],
'space': 'template'}})
bundle_info = abd.default_bd() + \
old_vof_paf_cst_slf_definitions + \
callosal_bd
Run GroupAFQ with these parameters#
Finally, we can run GroupAFQ with the 2.0 parameters. In sum, we changed: Tractography parameters to use CSD and seed throughout the white matter; PVE images to use FA thresholding; Bundle definitions for VOF, pAF, and CST to use the old definitions; Callosal bundles to use mahalanobis cleaning.
myafq = GroupAFQ(
bids_path=op.join(afd.afq_home, 'stanford_hardi'),
dwi_preproc_pipeline='vistasoft',
t1_preproc_pipeline='freesurfer',
tracking_params=tracking_params,
brain_mask_definition=bm_def,
pve=pve,
bundle_info=bundle_info)
myafq.export_all()
INFO:bidsschematools:No schema path specified, defaulting to the bundled schema, `/opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/bidsschematools/data/schema.json`.
INFO:AFQ:Using the following files for subject 01 and session 01:
INFO:AFQ: DWI: /home/runner/AFQ_data/stanford_hardi/derivatives/vistasoft/sub-01/ses-01/dwi/sub-01_ses-01_dwi.nii.gz
INFO:AFQ: BVAL: /home/runner/AFQ_data/stanford_hardi/derivatives/vistasoft/sub-01/ses-01/dwi/sub-01_ses-01_dwi.bval
INFO:AFQ: BVEC: /home/runner/AFQ_data/stanford_hardi/derivatives/vistasoft/sub-01/ses-01/dwi/sub-01_ses-01_dwi.bvec
INFO:AFQ: T1: /home/runner/AFQ_data/stanford_hardi/derivatives/freesurfer/sub-01/ses-01/anat/sub-01_ses-01_T1w.nii.gz
INFO:AFQ:Calculating _desc-brain_mask.nii.gz...
INFO:AFQ:Calculating _desc-T1w_mask.nii.gz...
INFO:AFQ:_desc-T1w_mask.nii.gz completed. Saving to /home/runner/AFQ_data/stanford_hardi/derivatives/afq/sub-01/ses-01/dwi/sub-01_ses-01_desc-T1w_mask.nii.gz
INFO:AFQ:_desc-brain_mask.nii.gz completed. Saving to /home/runner/AFQ_data/stanford_hardi/derivatives/afq/sub-01/ses-01/dwi/sub-01_ses-01_desc-brain_mask.nii.gz
INFO:AFQ:Calculating _desc-masked_T1w.nii.gz...
INFO:AFQ:_desc-masked_T1w.nii.gz completed. Saving to /home/runner/AFQ_data/stanford_hardi/derivatives/afq/sub-01/ses-01/dwi/sub-01_ses-01_desc-masked_T1w.nii.gz
INFO:AFQ:Calculating affine pre-alignment...
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
Cell In[6], line 10
6 brain_mask_definition=bm_def,
7 pve=pve,
8 bundle_info=bundle_info)
9
---> 10 myafq.export_all()
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/api/group.py:720, in GroupAFQ.export_all(self, viz, afqbrowser, xforms, indiv)
694 """Exports all the possible outputs
695
696 Parameters
(...) 716 Default: True
717 """
718 start_time = time()
--> 720 export_all_helper(self, xforms, indiv, viz)
722 self.combine_profiles()
723 if afqbrowser:
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/api/utils.py:160, in export_all_helper(api_afq_object, xforms, indiv, viz)
158 if xforms:
159 try:
--> 160 api_afq_object.export("b0_warped")
161 except Exception as e:
162 api_afq_object.logger.warning(
163 (
164 "Failed to export warped b0. This could be because your "
(...) 167 )
168 )
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/api/group.py:647, in GroupAFQ.export(self, attr_name, collapse)
645 to_calc_list.append((subject, session))
646 else:
--> 647 results[subject][session] = val_from_plan(plans_dict, attr_name)
649 # if some need to be calculated, do those in parallel
650 if to_calc_list:
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/api/utils.py:146, in val_from_plan(plan, attr_name)
144 def val_from_plan(plan, attr_name):
145 try:
--> 146 return plan[attr_name]
147 except Exception as err:
148 current_err = err
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/pcollections/_lazy.py:381, in ldict.__getitem__(self, key)
379 v = pdict.__getitem__(self, key)
380 if isinstance(v, lazy):
--> 381 v = v()
382 return v
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/pcollections/_lazy.py:143, in lazy.__call__(self)
141 val = self.value
142 else:
--> 143 val = part()
144 # We've successfully calculated the value; set the members
145 # appropriately.
146 object.__setattr__(self, 'value', val)
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/immlib/workflow/_core.py:855, in plan._source_lookup(inputtup, calctup, src)
853 (cidx, oidx) = src
854 lazycalc = calctup[cidx]
--> 855 val = lazycalc()[oidx]
856 else:
857 val = inputtup[src]()
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/pcollections/_lazy.py:143, in lazy.__call__(self)
141 val = self.value
142 else:
--> 143 val = part()
144 # We've successfully calculated the value; set the members
145 # appropriately.
146 object.__setattr__(self, 'value', val)
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/immlib/workflow/_core.py:884, in plan._make_calctup.<locals>.<lambda>(c, args)
881 # We take advantage of Python's weak closures here:
882 calctup = ()
883 calctup = tuple(
--> 884 lazy(lambda c,args: f(inputtup, calctup, c, args), c, args)
885 for (c,args) in zip(calcdata.calcs, calcdata.args))
886 return calctup
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/immlib/workflow/_core.py:868, in plan._call_calc(inputtup, calctup, c, args)
866 kwargs = {}
867 c = to_calc(c)
--> 868 for (p,arg) in zip(c.signature.parameters.values(), argvals):
869 if p.kind == p.POSITIONAL_ONLY:
870 args.append[arg]
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/immlib/workflow/_core.py:855, in plan._source_lookup(inputtup, calctup, src)
853 (cidx, oidx) = src
854 lazycalc = calctup[cidx]
--> 855 val = lazycalc()[oidx]
856 else:
857 val = inputtup[src]()
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/pcollections/_lazy.py:143, in lazy.__call__(self)
141 val = self.value
142 else:
--> 143 val = part()
144 # We've successfully calculated the value; set the members
145 # appropriately.
146 object.__setattr__(self, 'value', val)
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/immlib/workflow/_core.py:884, in plan._make_calctup.<locals>.<lambda>(c, args)
881 # We take advantage of Python's weak closures here:
882 calctup = ()
883 calctup = tuple(
--> 884 lazy(lambda c,args: f(inputtup, calctup, c, args), c, args)
885 for (c,args) in zip(calcdata.calcs, calcdata.args))
886 return calctup
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/immlib/workflow/_core.py:873, in plan._call_calc(inputtup, calctup, c, args)
871 else:
872 kwargs[p.name] = arg
--> 873 r = c.eager_call(*args, **kwargs)
874 if is_amap(r):
875 return tuple(map(r.__getitem__, c.outputs))
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/immlib/workflow/_core.py:448, in calc.eager_call(self, *args, **kwargs)
432 """Eagerly calls the given calculation using the arguments.
433
434 ``c.eager_call(...)`` returns the result of calling the calculation
(...) 444 calc.eager_mapcall, calc.lazy_call, calc.lazy_mapcall
445 """
446 # Now we just pass these arguments along (the function itself has been
447 # given the caching code via decorators already).
--> 448 res = self.function(*args, **kwargs)
449 # Now interpret the result.
450 outs = self.outputs
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/tasks/mapping.py:132, in mapping(base_fname, dwi_data_file, reg_subject, data_imap, mapping_definition)
128 if not isinstance(mapping_definition, Definition):
129 raise TypeError(
130 "mapping must be a mapping defined" + " in `AFQ.definitions.mapping`"
131 )
--> 132 return mapping_definition.get_for_subses(
133 base_fname,
134 data_imap["dwi"],
135 dwi_data_file,
136 reg_subject,
137 reg_template,
138 tmpl_name,
139 )
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/definitions/mapping.py:339, in SynMap.get_for_subses(self, base_fname, dwi, dwi_data_file, reg_subject, reg_template, tmpl_name, subject_sls, template_sls)
337 start_time = time()
338 if self.use_prealign:
--> 339 reg_prealign = self.prealign(reg_subject, reg_template)
340 else:
341 reg_prealign = None
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/definitions/mapping.py:216, in GeneratedMapMixin.prealign(self, reg_subject, reg_template)
214 def prealign(self, reg_subject, reg_template):
215 logger.info("Calculating affine pre-alignment...")
--> 216 _, aff = affine_registration(reg_subject, reg_template, **self.affine_kwargs)
217 return aff
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/testing/decorators.py:201, in warning_for_keywords.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
194 # Check if the current version is within the warning range
195 if (
196 version.parse(from_version)
197 <= version.parse(current_version)
198 <= version.parse(until_version)
199 ):
200 # Convert positional to keyword arguments and issue a warning
--> 201 return convert_positional_to_keyword(func, args, kwargs)
203 # If the version is greater than the until_version,
204 # pass the arguments as they are
205 elif version.parse(current_version) > version.parse(until_version):
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/testing/decorators.py:192, in warning_for_keywords.<locals>.decorator.<locals>.wrapper.<locals>.convert_positional_to_keyword(func, args, kwargs)
182 warnings.warn(
183 f"Pass {positionally_passed_kwonly_args} as keyword args. "
184 f"From version {until_version} passing these as positional "
(...) 187 stacklevel=3,
188 )
190 return func(*positional_args, **corrected_kwargs)
--> 192 return func(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/align/_public.py:599, in affine_registration(moving, static, moving_affine, static_affine, pipeline, starting_affine, metric, level_iters, sigmas, factors, ret_metric, moving_mask, static_mask, optimizer_options, **metric_kwargs)
597 else:
598 transform = _METHOD_DICT[func][1]()
--> 599 xform, xopt, fopt = affreg.optimize(
600 static,
601 moving,
602 transform,
603 None,
604 static_grid2world=static_affine,
605 moving_grid2world=moving_affine,
606 starting_affine=starting_affine,
607 ret_metric=True,
608 static_mask=static_mask,
609 moving_mask=moving_mask,
610 )
611 starting_affine = xform.affine
613 # Copy the final affine into a final variable
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/testing/decorators.py:201, in warning_for_keywords.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
194 # Check if the current version is within the warning range
195 if (
196 version.parse(from_version)
197 <= version.parse(current_version)
198 <= version.parse(until_version)
199 ):
200 # Convert positional to keyword arguments and issue a warning
--> 201 return convert_positional_to_keyword(func, args, kwargs)
203 # If the version is greater than the until_version,
204 # pass the arguments as they are
205 elif version.parse(current_version) > version.parse(until_version):
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/testing/decorators.py:192, in warning_for_keywords.<locals>.decorator.<locals>.wrapper.<locals>.convert_positional_to_keyword(func, args, kwargs)
182 warnings.warn(
183 f"Pass {positionally_passed_kwonly_args} as keyword args. "
184 f"From version {until_version} passing these as positional "
(...) 187 stacklevel=3,
188 )
190 return func(*positional_args, **corrected_kwargs)
--> 192 return func(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/align/imaffine.py:1331, in AffineRegistration.optimize(***failed resolving arguments***)
1328 else:
1329 self.options["maxiter"] = max_iter
-> 1331 opt = Optimizer(
1332 self.metric.distance_and_gradient,
1333 self.params0,
1334 method=self.method,
1335 jac=True,
1336 options=self.options,
1337 )
1338 params = opt.xopt
1340 # Update starting_affine matrix with optimal parameters
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/testing/decorators.py:201, in warning_for_keywords.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
194 # Check if the current version is within the warning range
195 if (
196 version.parse(from_version)
197 <= version.parse(current_version)
198 <= version.parse(until_version)
199 ):
200 # Convert positional to keyword arguments and issue a warning
--> 201 return convert_positional_to_keyword(func, args, kwargs)
203 # If the version is greater than the until_version,
204 # pass the arguments as they are
205 elif version.parse(current_version) > version.parse(until_version):
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/testing/decorators.py:192, in warning_for_keywords.<locals>.decorator.<locals>.wrapper.<locals>.convert_positional_to_keyword(func, args, kwargs)
182 warnings.warn(
183 f"Pass {positionally_passed_kwonly_args} as keyword args. "
184 f"From version {until_version} passing these as positional "
(...) 187 stacklevel=3,
188 )
190 return func(*positional_args, **corrected_kwargs)
--> 192 return func(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/core/optimize.py:164, in Optimizer.__init__(self, fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options, evolution)
148 res = minimize(
149 fun,
150 x0,
(...) 160 options=options,
161 )
163 else:
--> 164 res = minimize(
165 fun,
166 x0,
167 args,
168 method,
169 jac,
170 hess,
171 hessp,
172 bounds,
173 constraints,
174 tol,
175 callback,
176 options,
177 )
179 self.res = res
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/scipy/optimize/_minimize.py:784, in minimize(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)
781 res = _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback,
782 **options)
783 elif meth == 'l-bfgs-b':
--> 784 res = _minimize_lbfgsb(fun, x0, args, jac, bounds,
785 callback=callback, **options)
786 elif meth == 'tnc':
787 res = _minimize_tnc(fun, x0, args, jac, bounds, callback=callback,
788 **options)
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/scipy/optimize/_lbfgsb_py.py:469, in _minimize_lbfgsb(fun, x0, args, jac, bounds, disp, maxcor, ftol, gtol, eps, maxfun, maxiter, iprint, callback, maxls, finite_diff_rel_step, workers, **unknown_options)
461 _lbfgsb.setulb(m, x, low_bnd, upper_bnd, nbd, f, g, factr, pgtol, wa,
462 iwa, task, lsave, isave, dsave, maxls, ln_task)
464 if task[0] == 3:
465 # The minimization routine wants f and g at the current x.
466 # Note that interruptions due to maxfun are postponed
467 # until the completion of the current minimization iteration.
468 # Overwrite f and g:
--> 469 f, g = func_and_grad(x)
470 elif task[0] == 1:
471 # new iteration
472 n_iterations += 1
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/scipy/optimize/_differentiable_functions.py:412, in ScalarFunction.fun_and_grad(self, x)
410 if not np.array_equal(x, self.x):
411 self._update_x(x)
--> 412 self._update_fun()
413 self._update_grad()
414 return self.f, self.g
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/scipy/optimize/_differentiable_functions.py:362, in ScalarFunction._update_fun(self)
360 def _update_fun(self):
361 if not self.f_updated:
--> 362 fx = self._wrapped_fun(self.x)
363 self._nfev += 1
364 if fx < self._lowest_f:
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/scipy/_lib/_util.py:603, in _ScalarFunctionWrapper.__call__(self, x)
600 def __call__(self, x):
601 # Send a copy because the user may overwrite it.
602 # The user of this class might want `x` to remain unchanged.
--> 603 fx = self.f(np.copy(x), *self.args)
604 self.nfev += 1
606 # Make sure the function returns a true scalar
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/scipy/optimize/_optimize.py:80, in MemoizeJac.__call__(self, x, *args)
78 def __call__(self, x, *args):
79 """ returns the function value """
---> 80 self._compute_if_needed(x, *args)
81 return self._value
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/scipy/optimize/_optimize.py:74, in MemoizeJac._compute_if_needed(self, x, *args)
72 if not np.all(x == self.x) or self._value is None or self.jac is None:
73 self.x = np.asarray(x).copy()
---> 74 fg = self.fun(x, *args)
75 self.jac = fg[1]
76 self._value = fg[0]
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/align/imaffine.py:888, in MutualInformationMetric.distance_and_gradient(self, params)
868 r"""Numeric value of the metric and its gradient at given parameters.
869
870 Parameters
(...) 885
886 """
887 try:
--> 888 self._update_mutual_information(params, update_gradient=True)
889 except (AffineInversionError, AffineInvalidValuesError):
890 return np.inf, 0 * self.metric_grad
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/testing/decorators.py:201, in warning_for_keywords.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
194 # Check if the current version is within the warning range
195 if (
196 version.parse(from_version)
197 <= version.parse(current_version)
198 <= version.parse(until_version)
199 ):
200 # Convert positional to keyword arguments and issue a warning
--> 201 return convert_positional_to_keyword(func, args, kwargs)
203 # If the version is greater than the until_version,
204 # pass the arguments as they are
205 elif version.parse(current_version) > version.parse(until_version):
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/testing/decorators.py:192, in warning_for_keywords.<locals>.decorator.<locals>.wrapper.<locals>.convert_positional_to_keyword(func, args, kwargs)
182 warnings.warn(
183 f"Pass {positionally_passed_kwonly_args} as keyword args. "
184 f"From version {until_version} passing these as positional "
(...) 187 stacklevel=3,
188 )
190 return func(*positional_args, **corrected_kwargs)
--> 192 return func(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/align/imaffine.py:782, in MutualInformationMetric._update_mutual_information(self, params, update_gradient)
777 if self.sampling_proportion is None: # Dense case
778 # Compute the gradient of moving img. at physical points
779 # associated with the >>static image's grid<< cells
780 # The image gradient must be eval. at current moved points
781 grid_to_world = current_affine.dot(self.static_grid2world)
--> 782 mgrad, inside = vf.gradient(
783 self.moving,
784 self.moving_world2grid,
785 self.moving_spacing,
786 self.static.shape,
787 grid_to_world,
788 )
789 # The Jacobian must be evaluated at the pre-aligned points
790 H.update_gradient_dense(
791 params,
792 self.transform,
(...) 798 mmask=moving_mask_values,
799 )
KeyboardInterrupt: