How to segment out only some bundles#

The pyAFQ software can be configured to find all of its default set of white matter pathways, or bundles. Alternatively, it can be configured to find only some bundles. This example shows how to track and recognize only certain bundles that you are interested in, though note that ARC, pARC and VOF are all also part of the set of bundles that are segmented per default.

import AFQ.api.bundle_dict as abd
import AFQ.data.fetch as afd
from AFQ.api.group import GroupAFQ
from AFQ.definitions.image import RoiImage
import AFQ.utils.streamlines as aus

import os.path as op

afd.organize_stanford_data(clear_previous_afq="track")

bundle_names = [
    "Left Arcuate", "Right Arcuate",
    "Left Posterior Arcuate", "Right Posterior Arcuate",
    "Left Vertical Occipital", "Right Vertical Occipital"]
bundle_dict = abd.default_bd()[bundle_names]


myafq = GroupAFQ(
    op.join(afd.afq_home, 'stanford_hardi'),
    bundle_info=bundle_dict,
    dwi_preproc_pipeline='vistasoft',
    t1_preproc_pipeline='freesurfer',
    tracking_params={
        "n_seeds": 50000,
        "random_seeds": True,
        "seed_mask": RoiImage(use_waypoints=True, use_endpoints=True),
    }
)

for b_name in bundle_names:
    b_len = len(aus.SegmentedSFT.fromfile(myafq.export("bundles")[
        "01"]).get_bundle(b_name))

    if b_len < 1:
        raise ValueError(f"{b_name} not found")

myafq.export("all_bundles_figure")["01"][0]
2026-05-19 01:02:59,739	INFO util.py:154 -- Missing packages: ['ipywidgets']. Run `pip install -U ipywidgets`, then restart the notebook server for rich notebook output.
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
WARNING:AFQ:It is recommended to provide CSF/GM/WM segmentations using PVEImage or PVEImages in AFQ.definitions.image. Otherwise, SynthSeg2 will be used
INFO:AFQ:Calculating _desc-bundles_tractography...
INFO:AFQ:Calculating _desc-seed_mask.nii.gz...
2026-05-19 01:03:07.250668081 [W:onnxruntime:Default, device_discovery.cc:133 GetPciBusId] Skipping pci_bus_id for PCI path at "/sys/devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0004:00/MSFT1000:00/5620e0c7-8062-4dce-aeb7-520c7ef76171" because filename "5620e0c7-8062-4dce-aeb7-520c7ef76171" did not match expected pattern of [0-9a-f]+:[0-9a-f]+:[0-9a-f]+[.][0-9a-f]+
INFO:AFQ:Calculating affine pre-alignment...
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[1], line 31
     27     }
     28 )
     29 
     30 for b_name in bundle_names:
---> 31     b_len = len(aus.SegmentedSFT.fromfile(myafq.export("bundles")[
     32         "01"]).get_bundle(b_name))
     33 
     34     if b_len < 1:

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: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/decorators.py:104, in as_file.<locals>._as_file.<locals>.wrapper_as_file(*args, **kwargs)
    101 logger.info(f"Calculating {calculation_name}...")
    103 try:
--> 104     results = func(*args, **kwargs)
    106     if len(output_specs) == 1:
    107         results = [results]

File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/tasks/segmentation.py:56, in segment(structural_imap, data_imap, mapping_imap, tractography_imap, segmentation_params)
     54 bundle_dict = data_imap["bundle_dict"]
     55 reg_template = data_imap["reg_template"]
---> 56 streamlines = tractography_imap["streamlines"]
     57 if (
     58     streamlines.endswith(".trk")
     59     or streamlines.endswith(".tck")
     60     or streamlines.endswith(".vtk")
     61 ):
     62     tg = load_tractogram(streamlines, data_imap["dwi"], bbox_valid_check=False)

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/decorators.py:104, in as_file.<locals>._as_file.<locals>.wrapper_as_file(*args, **kwargs)
    101 logger.info(f"Calculating {calculation_name}...")
    103 try:
--> 104     results = func(*args, **kwargs)
    106     if len(output_specs) == 1:
    107         results = [results]

File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/AFQ/definitions/image.py:402, in RoiImage.get_image_getter.<locals>._image_getter_helper(mapping_imap, data_imap, structural_imap, tissue_imap, segmentation_params)
    398     bundle_dict = bundle_dict
    400 for bundle_name in bundle_dict:
    401     bundle_entry = bundle_dict.transform_rois(
--> 402         bundle_name, mapping_imap["mapping"], data_imap["dwi"]
    403     )
    404     rois = {}
    405     if self.use_endpoints:

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: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:769, in MutualInformationMetric._update_mutual_information(self, params, update_gradient)
    765 self.affine_map.set_affine(current_affine)
    767 # Update the histogram with the current joint intensities
    768 static_values, moving_values, static_mask_values, moving_mask_values = (
--> 769     self._update_histogram()
    770 )
    772 H = self.histogram  # Shortcut to `self.histogram`
    773 grad = None  # Buffer to write the MI gradient into (if needed)

File /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/dipy/align/imaffine.py:718, in MutualInformationMetric._update_histogram(self)
    713     if self.moving_mask is not None:
    714         moving_mask_values = self.affine_map.transform(
    715             self.moving_mask, interpolation="nearest"
    716         ).astype(np.int32)
--> 718     self.histogram.update_pdfs_dense(
    719         static_values,
    720         moving_values,
    721         smask=self.static_mask,
    722         mmask=moving_mask_values,
    723     )
    724 else:  # Sparse case
    725     sp_to_moving = self.moving_world2grid.dot(self.affine_map.affine)

KeyboardInterrupt: