graphfla 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. graphfla-0.1.0/LICENSE +21 -0
  2. graphfla-0.1.0/MANIFEST.in +3 -0
  3. graphfla-0.1.0/PKG-INFO +47 -0
  4. graphfla-0.1.0/README.md +166 -0
  5. graphfla-0.1.0/graphfla/__init__.py +87 -0
  6. graphfla-0.1.0/graphfla/_neighbors.py +143 -0
  7. graphfla-0.1.0/graphfla/_processor.py +657 -0
  8. graphfla-0.1.0/graphfla/algorithms/__init__.py +11 -0
  9. graphfla-0.1.0/graphfla/algorithms/adaptive_walk.py +137 -0
  10. graphfla-0.1.0/graphfla/algorithms/random_walk.py +76 -0
  11. graphfla-0.1.0/graphfla/analysis/__init__.py +99 -0
  12. graphfla-0.1.0/graphfla/analysis/correlation.py +274 -0
  13. graphfla-0.1.0/graphfla/analysis/epistasis.py +1019 -0
  14. graphfla-0.1.0/graphfla/analysis/fitness.py +212 -0
  15. graphfla-0.1.0/graphfla/analysis/navigability.py +532 -0
  16. graphfla-0.1.0/graphfla/analysis/robustness.py +267 -0
  17. graphfla-0.1.0/graphfla/analysis/ruggedness.py +246 -0
  18. graphfla-0.1.0/graphfla/distances.py +113 -0
  19. graphfla-0.1.0/graphfla/filters.py +306 -0
  20. graphfla-0.1.0/graphfla/landscape.py +2219 -0
  21. graphfla-0.1.0/graphfla/lon.py +510 -0
  22. graphfla-0.1.0/graphfla/problems/__init__.py +22 -0
  23. graphfla-0.1.0/graphfla/problems/base_problem.py +110 -0
  24. graphfla-0.1.0/graphfla/problems/biological.py +398 -0
  25. graphfla-0.1.0/graphfla/problems/combinatorial.py +346 -0
  26. graphfla-0.1.0/graphfla/sampling.py +186 -0
  27. graphfla-0.1.0/graphfla/utils.py +149 -0
  28. graphfla-0.1.0/graphfla.egg-info/PKG-INFO +47 -0
  29. graphfla-0.1.0/graphfla.egg-info/SOURCES.txt +34 -0
  30. graphfla-0.1.0/graphfla.egg-info/dependency_links.txt +1 -0
  31. graphfla-0.1.0/graphfla.egg-info/requires.txt +7 -0
  32. graphfla-0.1.0/graphfla.egg-info/top_level.txt +1 -0
  33. graphfla-0.1.0/pyproject.toml +3 -0
  34. graphfla-0.1.0/setup.cfg +4 -0
  35. graphfla-0.1.0/setup.py +46 -0
  36. graphfla-0.1.0/tests/test.py +467 -0
graphfla-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 COLA Laboratory
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ exclude data/*
2
+ recursive-exclude data *
3
+ include LICENSE
@@ -0,0 +1,47 @@
1
+ Metadata-Version: 2.4
2
+ Name: graphfla
3
+ Version: 0.1.0
4
+ Summary: A Python package for Graph-based Fitness Landscape Analysis.
5
+ Home-page: https://github.com/COLA-Laboratory/GraphFLA/tree/main
6
+ Author: Mingyu Huang
7
+ Author-email: m.huang.gla@outlook.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Topic :: Scientific/Engineering
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
18
+ Classifier: Development Status :: 3 - Alpha
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/plain
21
+ License-File: LICENSE
22
+ Requires-Dist: joblib>=1.0.0
23
+ Requires-Dist: numpy>=1.19
24
+ Requires-Dist: pandas>=1.1
25
+ Requires-Dist: python-igraph>=0.9
26
+ Requires-Dist: scikit-learn>=0.24
27
+ Requires-Dist: scipy>=1.6.0
28
+ Requires-Dist: tqdm>=4.40
29
+ Dynamic: author
30
+ Dynamic: author-email
31
+ Dynamic: classifier
32
+ Dynamic: description
33
+ Dynamic: description-content-type
34
+ Dynamic: home-page
35
+ Dynamic: license-file
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
39
+
40
+
41
+ graphfla: A Python package for Graph-based Fitness Landscape Analysis.
42
+ ========================================================
43
+ graphfla provides tools for generating, constructing, analyzing and
44
+ manipulating fitness landscapes commonly encountered in evolutionary biology
45
+ and black-box optimization. It includes a variety of features chacterizing
46
+ different aspects of fitness landscape topography, such as ruggedness,
47
+ navigability, neutrality, and epistasis.
@@ -0,0 +1,166 @@
1
+ # GraphFLA
2
+
3
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
4
+ <!-- [![Python Versions](https://img.shields.io/pypi/pyversions/graphfla.svg)](https://pypi.python.org/pypi/graphfla/)
5
+ [![Issues](https://img.shields.io/github/issues/yourusername/graphfla.svg)](https://github.com/yourusername/graphfla/issues)
6
+ [![Stars](https://img.shields.io/github/stars/yourusername/graphfla.svg)](https://github.com/yourusername/graphfla/stargazers) -->
7
+
8
+ ![Alt text](images/landscape.jpg)
9
+
10
+ **GraphFLA** (Graph-based Fitness Landscape Analysis) is a Python framework for constructing, analyzing, manipulating and visualizing **fitness landscapes** as graphs. It provides a broad collection of features rooted in evolutoinary biology to decipher the topography of compelx fitness landscapes of diverse modalities.
11
+
12
+ ## Key Features
13
+ - **Versatility:** applicable to arbitrary discrete, combinatorial sequence-fitness data, ranging from biomolecules like DNA, RNA, and protein, to functional units like genes, to complex ecological communities.
14
+ - **Comprehensiveness:** offers a holistic collection of 20+ features for characterizing 4 fundamental topographical aspects of fitness landscape, including ruggedness, navigability, epistassi and neutrality.
15
+ - **Interoperability:** works with the same data format (i.e., `X` and `f`) as in training machine learning (ML) models, thus interoperable with established ML ecosystems in different disciplines.
16
+ - **Scalability:** heavily optimized to be capable of handling landscapes with even millions of variants.
17
+ - **Extensibility:** new landscape features can be easily added via an unified API.
18
+
19
+
20
+ ## Quick Start
21
+
22
+ Our documentation website is currently under development, but `GraphFLA` is quite easy to get started with!
23
+
24
+ ### 1. Prepare your data
25
+
26
+ `GraphFLA` is designed to interoperate with established ML frameworks and benchmarks by using the same data format as in ML model training: an `X` and an `f`.
27
+
28
+ Specifically, `X` can either be a list of sequences of strings representing genotypes, or a `pd.DataFrame` or an `numpy.ndarray`, wherein each column represents a loci; `f` can either be a list, `pd.Series` or `numpy.ndarray`.
29
+
30
+ To make landscape construction faster, we recommended removing redundant loci in `X` (i.e., those that are never mutated across the whole library) .
31
+
32
+ ```python
33
+ import pandas as pd
34
+
35
+ data = pd.read_csv("path_to_data.csv")
36
+
37
+ X = data["sequences"]
38
+ f = data["fitness"]
39
+ ```
40
+
41
+ ### 2. Create the landscape object
42
+
43
+ Creating a landscape object in `GraphFLA` is much like training an ML model: we first initialize a `Landscape` class, and then build it with our data.
44
+
45
+ Here, assume we are working with DNA sequences. `GraphFLA` provides registered methods for performance optimization for this type, which can be triggered by specifying `type="dna"`. Alternatively, you can directly use the `DNALandscape` class to get the same effect, which is natively built for DNA data.
46
+
47
+ The `maximize` parameter specifies the direction of optimization, i.e., whether `f` is to be optimized or minimized.
48
+
49
+ ```python
50
+ from graphfla.landscape import Landscape
51
+
52
+ # initialize the landscape
53
+ # this is equivalent to:
54
+ # from graphfla.landscape import DNALandscape
55
+ # landscape = DNALandscape(maximize=True)
56
+ landscape = Landscape(type="dna", maximize=True)
57
+
58
+ # build the landscape with our data
59
+ landscape.build_from_data(X, f, verbose=True)
60
+ ```
61
+
62
+ ### 3. Landscape analysis
63
+
64
+ Once the landscape is constructed, we can then analyze its features using the available functions (see later).
65
+
66
+ ```python
67
+ from graphfla.analysis import (
68
+ lo_ratio,
69
+ classify_epistasis,
70
+ r_s_ratio,
71
+ neutrality,
72
+ global_optima_accessibility,
73
+ )
74
+
75
+ local_optima_ratio = lo_ratio(landscape)
76
+ epistasis = classify_epistasis(landscape)
77
+ r_s_score = r_s_ratio(landscape)
78
+ neutrality_index = neutrality(landscape)
79
+ go_access = global_optima_accessibility(landscape)
80
+ ```
81
+ ### 4. Playing with arbitrary combinatorial data
82
+ The `type` parameter of the `Landscape` class currently supports `"dna"`, `rna`, `"protein"`, and `"boolean"`. However, this does not mean that `GraphFLA` can only work with these types of data; instead, these registered values are only for convenience and performance optimization purpose.
83
+
84
+ In fact, `GraphFLA` can handle arbitrary combinatorial search space as long as the values of each variable is discrete. To work with such data, we can initialize a general landscape, and then pass in a dictionary to specify the data type of each variable (options: `{"ordinal", "cateogrical", "boolean"}`).
85
+
86
+ ```python
87
+ import pandas as pd
88
+ from graphfla.landscape import Landscape
89
+
90
+ complex_data = pd.read_csv("path_to_complex_data.csv")
91
+
92
+ f = complex_data["fitness"]
93
+ # data serving as "X"
94
+ complex_search_space = complex_data.drop(columns=["fitness"])
95
+
96
+ # initialize a general fitness landscape without specifying `type`
97
+ landscape = Landscape(maximize=True)
98
+
99
+ # create a data type dictionary
100
+ data_types = {
101
+ "x1": "ordinal",
102
+ "x2": "categorical",
103
+ "x3": "boolean",
104
+ "x4": "categorical"
105
+ }
106
+
107
+ # build the landscape with our data and specified data types
108
+ landscape.build_from_data(X, f, data_types=data_types, verbose=True)
109
+ ```
110
+
111
+ ## Landscape Analysis Features
112
+
113
+ `GraphFLA` currently supports the following features for landscape analysis.
114
+
115
+ | **Class** | **Function** | **Feature** | **Range** | **Higher value indicates** |
116
+ |--------------------------|----------------------------------|----------------------------------------|---------------|----------------------------------------|
117
+ | **Ruggedness** | `lo_ratio` | Fraction of local optima | [0,1] | ↑ more peaks |
118
+ | | `r_s_ratio` | Roughness-slope ratio | [0, ∞) | ↑ ruggedness |
119
+ | | `autocorrelation` | Autocorrelation | [-1, 1] | ↓ ruggedness |
120
+ | | `gamma_statistic` | Gamma statistic | [-1, 1] | ↑ ruggedness |
121
+ | | `gamma_statistic` | Gamma star statistic | [-1, 1] | ↑ ruggedness |
122
+ | | `neighbor_fit_corr` | Neighbor-fitness correlation | [-1, 1] | ↓ ruggedness |
123
+ | **Epistasis** | `classify_epistasis` | Magnitude epistasis | [0, 1) | ↓ evolutionary constraints |
124
+ | | `classify_epistasis` | Sign epistasis | [0, 1] | ↑ evolutionary constraints |
125
+ | | `classify_epistasis` | Reciprocal sign epistasis | [0, 1] | ↑ evolutionary constraints |
126
+ | | `classify_epistasis` | Positive epistasis | [0, 1] | ↑ synergistic effects |
127
+ | | `classify_epistasis` | Negative epistasis | [0, 1] | ↑ antagonistic effects |
128
+ | | `global_idiosyncratic_index` | Global idiosyncratic index | [0, 1] | ↑ specific interactions |
129
+ | | `diminishing_returns_index` | Diminishing return epistasis | [0, 1] | ↑ flat peaks |
130
+ | | `increasing_costs_index` | Increasing cost epistasis | [0, 1] | ↑ steep descents |
131
+ | | `higher_order_epistasis` | Higher-order epistasis | [0, 1] | ↓ higher-order interactions |
132
+ | **Navigability** | `fitness_distance_corr` | Fitness-distance correlation | [-1, 1] | ↑ navigation |
133
+ | | `go_accessibility` | Global optima accessibility | [0, 1] | ↑ access to global peaks |
134
+ | | `basin_fit_corr` | Basin-fitness corr. (accessible) | [-1, 1] | ↑ access to fitter peaks |
135
+ | | `basin_fit_corr` | Basin-fitness corr. (greedy) | [-1, 1] | ↑ access to fitter peaks |
136
+ | | `calculate_evol_enhance` | Evol-enhancing mutation | [0, 1] | ↑ evolvability |
137
+ | **Neutrality** | `neutrality` | Neutrality | [0, 1] | ↑ neutrality |
138
+ | **Fitness Distribution** | `fitness_distribution` | Skewness | (-∞, ∞) | ↑ asymmetry of fitness values |
139
+ | | `fitness_distribution` | Kurtosis | (-∞, ∞) | ↑ outlier/extreme value prevalence |
140
+ | | `fitness_distribution` | Coefficient of variation (CV) | [0, ∞) | ↑ relative fitness variability |
141
+ | | `fitness_distribution` | Quartile coefficient | [0, 1] | ↑ interquartile dispersion |
142
+ | | `fitness_distribution` | Median/Mean ratio | [0, ∞) | ↑ deviation from symmetry |
143
+ | | `fitness_distribution` | Relative range | [0, ∞) | ↑ spread of fitness values |
144
+ | | `fitness_distribution` | Cauchy location parameter | (-∞, ∞) | ↑ central tendency estimate |
145
+
146
+
147
+ ## Landscape Classes
148
+
149
+ `GraphFLA` currently offers the following classes for landscape construction.
150
+
151
+ |**Classes**|**Supported search space**|**Description**|
152
+ |--|--|--|
153
+ |`Landscape`|All discrete, combinatorial spaces, where each variable can be either categorical, boolean, or ordinal|The base landscape class, most generalizable|
154
+ |`SequenceLandscape`|Categorical data where each variable takes values from the same alphabet.|Class optimized for general sequence data|
155
+ |`BooleanLandscape`|Boolean space|Class optimized for boolean data|
156
+ |`DNALandscape`|DNA sequence space|Class optimized for DNA data|
157
+ |`RNALandscape`|RNA sequence space|Class optimized for RNA data|
158
+ |`ProteinLandscape`|Protein sequence space|Class optimized for protein data|
159
+
160
+ ## License
161
+
162
+ This project is licensed under the terms of the [MIT License](./LICENSE).
163
+
164
+ ---
165
+
166
+ **Happy analyzing!** If you have any questions or suggestions, feel free to open an issue or start a discussion.
@@ -0,0 +1,87 @@
1
+ # graphfla/__init__.py
2
+
3
+ """
4
+ graphfla: A Python package for Graph-based Fitness Landscape Analysis.
5
+ ========================================================
6
+
7
+ graphfla provides tools for generating, analyzing, simulating evolution on,
8
+ and visualizing fitness landscapes, commonly encountered in evolutionary
9
+ computation, biology, optimization, and machine learning model training dynamics.
10
+
11
+ It aims to offer a modular and user-friendly interface for researchers and
12
+ practitioners working with sequence spaces, combinatorial spaces, and
13
+ their associated fitness functions.
14
+ """
15
+
16
+ # Authors: [Mingyu Huang, COLALab@UoE]
17
+
18
+ import importlib
19
+ import logging
20
+ import os
21
+ import random
22
+
23
+ __version__ = "0.1.dev0"
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ _exported_config_functions = []
28
+
29
+ _exported_core_objects = ["Landscape"]
30
+
31
+
32
+ # List of submodules and top-level utility modules to be accessible
33
+ # via lazy loading (e.g., graphfla.analysis, graphfla.utils)
34
+ _submodules = [
35
+ "analysis",
36
+ "algorithms",
37
+ "distances",
38
+ "landscape",
39
+ "lon",
40
+ "plotting",
41
+ "problems",
42
+ "sampling",
43
+ "filters" "utils",
44
+ ]
45
+
46
+ __all__ = _submodules + _exported_config_functions + _exported_core_objects
47
+
48
+
49
+ def __dir__():
50
+ """Provides controlled module listing for autocompletion."""
51
+ return __all__
52
+
53
+
54
+ def __getattr__(name):
55
+ """
56
+ Lazily imports submodules and top-level modules upon first access.
57
+
58
+ Example:
59
+ >>> import graphfla
60
+ >>> graphfla.analysis.fdc # analysis submodule is imported here
61
+ """
62
+ if name in _submodules:
63
+ return importlib.import_module(f".{name}", __name__)
64
+ elif name in _exported_core_objects or name in _exported_config_functions:
65
+ try:
66
+ return globals()[name]
67
+ except KeyError:
68
+ raise AttributeError(f"Module '{__name__}' has no attribute '{name}'")
69
+ else:
70
+ try:
71
+ return globals()[name]
72
+ except KeyError:
73
+ raise AttributeError(f"Module '{__name__}' has no attribute '{name}'")
74
+
75
+
76
+ def setup_module(module):
77
+ """Fixture for the tests to assure globally controllable seeding of RNGs."""
78
+ import numpy as np
79
+
80
+ _random_seed = os.environ.get("GRAPHFLA_SEED", None)
81
+ if _random_seed is None:
82
+ _random_seed = np.random.uniform() * np.iinfo(np.int32).max
83
+ _random_seed = int(_random_seed)
84
+
85
+ logger.info("I: Seeding RNGs with %r", _random_seed)
86
+ np.random.seed(_random_seed)
87
+ random.seed(_random_seed)
@@ -0,0 +1,143 @@
1
+ from typing import Protocol, Tuple, Dict, List, Any, runtime_checkable
2
+ import warnings
3
+
4
+
5
+ @runtime_checkable
6
+ class NeighborGenerator(Protocol):
7
+ """Protocol defining the interface for neighbor generation."""
8
+
9
+ def generate(
10
+ self, config: Tuple, config_dict: Dict, n_edit: int = 1
11
+ ) -> List[Tuple]:
12
+ """
13
+ Generate neighbors for a given configuration.
14
+
15
+ Parameters
16
+ ----------
17
+ config : tuple
18
+ The configuration for which to find neighbors
19
+ config_dict : dict
20
+ Dictionary describing the encoding
21
+ n_edit : int
22
+ Edit distance for neighborhood definition
23
+
24
+ Returns
25
+ -------
26
+ list[tuple]
27
+ List of neighboring configurations
28
+ """
29
+ ...
30
+
31
+
32
+ class BooleanNeighborGenerator:
33
+ """Generator for boolean neighbors (bit flips)."""
34
+
35
+ def generate(
36
+ self, config: Tuple, config_dict: Dict, n_edit: int = 1
37
+ ) -> List[Tuple]:
38
+ """Generate neighbors by flipping bits."""
39
+ if n_edit != 1:
40
+ warnings.warn(
41
+ f"BooleanNeighborGenerator only supports n_edit=1 for single bit flips. "
42
+ f"Received n_edit={n_edit}. Returning no neighbors.",
43
+ UserWarning,
44
+ )
45
+ return []
46
+
47
+ neighbors = []
48
+ current_config_list = list(config)
49
+ num_bits = len(current_config_list)
50
+
51
+ for i in range(num_bits):
52
+ neighbor_list = current_config_list.copy()
53
+ neighbor_list[i] = 1 - neighbor_list[i] # Flip bit
54
+ neighbors.append(tuple(neighbor_list))
55
+
56
+ return neighbors
57
+
58
+
59
+ class SequenceNeighborGenerator:
60
+ """Generator for sequence neighbors (substitutions)."""
61
+
62
+ def __init__(self, alphabet_size: int):
63
+ """
64
+ Initialize with the size of the alphabet.
65
+
66
+ Parameters
67
+ ----------
68
+ alphabet_size : int
69
+ Number of possible values at each position
70
+ """
71
+ self.alphabet_size = alphabet_size
72
+
73
+ def generate(
74
+ self, config: Tuple, config_dict: Dict, n_edit: int = 1
75
+ ) -> List[Tuple]:
76
+ """Generate neighbors by substituting at each position."""
77
+ if n_edit != 1:
78
+ warnings.warn(
79
+ f"SequenceNeighborGenerator only supports n_edit=1 for single position substitutions. "
80
+ f"Received n_edit={n_edit}. Returning no neighbors.",
81
+ UserWarning,
82
+ )
83
+ return []
84
+
85
+ neighbors = []
86
+ current_config_list = list(config)
87
+ num_positions = len(current_config_list)
88
+
89
+ for i in range(num_positions):
90
+ original_val = current_config_list[i]
91
+ # Try each possible substitution at this position
92
+ for new_val in range(self.alphabet_size):
93
+ if new_val != original_val:
94
+ neighbor_list = current_config_list.copy()
95
+ neighbor_list[i] = new_val
96
+ neighbors.append(tuple(neighbor_list))
97
+
98
+ return neighbors
99
+
100
+
101
+ class DefaultNeighborGenerator:
102
+ """Default generator for mixed data types."""
103
+
104
+ def generate(
105
+ self, config: Tuple, config_dict: Dict, n_edit: int = 1
106
+ ) -> List[Tuple]:
107
+ """Generate neighbors based on data types in config_dict."""
108
+ if n_edit != 1:
109
+ warnings.warn(
110
+ f"DefaultNeighborGenerator only fully supports n_edit=1. "
111
+ f"Received n_edit={n_edit}.",
112
+ UserWarning,
113
+ )
114
+
115
+ neighbors = []
116
+ num_vars = len(config)
117
+
118
+ for i in range(num_vars):
119
+ info = config_dict[i]
120
+ current_val = config[i]
121
+ dtype = info["type"]
122
+
123
+ if dtype == "boolean":
124
+ # Flip the bit (0 to 1, 1 to 0)
125
+ new_vals = [1 - current_val]
126
+ elif dtype in ["categorical", "ordinal"]:
127
+ # Iterate through all possible values
128
+ max_val = info["max"]
129
+ new_vals = [v for v in range(max_val + 1) if v != current_val]
130
+ else:
131
+ warnings.warn(
132
+ f"Unsupported dtype '{dtype}' in generate_neighbors, skipping var {i}",
133
+ RuntimeWarning,
134
+ )
135
+ continue
136
+
137
+ # Create neighbor tuples
138
+ for new_val in new_vals:
139
+ neighbor_list = list(config)
140
+ neighbor_list[i] = new_val
141
+ neighbors.append(tuple(neighbor_list))
142
+
143
+ return neighbors