Skip to content

Repository files navigation

GraphFLA

GraphFLA


GraphFLA (Graph-based Fitness Landscape Analysis) is a Python framework for constructing, analyzing, manipulating and visualizing fitness landscapes of combinatorial optimization problems. Drawing on concepts and measures established in evolutionary biology, it represents a landscape as a graph of variants connected by single mutations and characterizes the topography of complex optimization landscapes across diverse fields.

This is also the official code and data repository for the NeurIPS 2025 (Spotlight) paper "Augmenting Biological Fitness Prediction Benchmarks with Landscape Features from GraphFLA".

Full documentation, including the API reference, is at colalab.ai/GraphFLA.

Tutorials

These twelve notebooks include worked code and saved outputs, with their input data alongside them. Each one also opens in Google Colab, where it installs GraphFLA and downloads its data.

Dataset Application Colab
GB1 Protein variants and antibody binding Open In Colab
DHFR Gene variants and antibiotic resistance Open In Colab
Ribozymes RNA variants and catalytic activity Open In Colab
Suzuki-Miyaura Chemical reaction conditions Open In Colab
Flow semihydrogenation Electrochemical process settings Open In Colab
W-Re-Os alloys Alloy composition Open In Colab
Perovskites Material constituent choices Open In Colab
BacPUS Bacterial strain-substrate combinations Open In Colab
Cyanimide Chemical building blocks and enzyme inhibition Open In Colab
NCI-ALMANAC Drug combinations and doses Open In Colab
NAS-Bench-201 Neural architecture choices Open In Colab
LLVM Compiler configuration Open In Colab

Key Features

  • Versatility: GraphFLA applies to any discrete, combinatorial mapping from variants to fitness, from biological sequences to chemical reaction conditions, material compositions, drug combinations and software configurations.
  • Comprehensiveness: more than 20 metrics, drawn from the evolutionary biology and evolutionary computation literature, quantify ruggedness, epistasis, navigability and neutrality, together giving a detailed account of landscape topography.
  • Interoperability: GraphFLA analyzes the same variants X and fitness values f used to train machine learning models, so landscape analysis fits into existing modeling workflows without data conversion.
  • Scalability: GraphFLA constructs and analyzes landscapes comprising millions of variants.

Quick Start

1. Install

pip install graphfla

2. Build a landscape

GraphFLA takes the same input as a machine learning model: variants X and their fitness f. X can be a list of sequences, or a pandas.DataFrame or numpy.ndarray with one column per position; f can be a list, pandas.Series or numpy.ndarray.

Choose the class that matches your data:

Data Class
DNA, RNA or protein sequences DNALandscape, RNALandscape, ProteinLandscape
Sequences over another alphabet SequenceLandscape
Binary variables (on/off, present/absent) BooleanLandscape
Ordered levels (doses, temperatures) OrdinalLandscape
A mix of the above Landscape (see step 4)
from graphfla.landscape import DNALandscape

# 8 variants over 3 positions, with a single peak at GGG
X = ["AAA", "AAG", "AGA", "AGG", "GAA", "GAG", "GGA", "GGG"]
f = [0.10, 0.25, 0.25, 0.40, 0.25, 0.40, 0.40, 0.91]

landscape = DNALandscape(maximize=True)  # maximize=False if lower f is fitter
landscape.build_from_data(X, f)

3. Analyze it

analysis.profile() computes every landscape-level metric in one call and returns a pandas.Series. Given a list of landscapes, it returns a DataFrame with one row each. Sampling-based metrics such as classify_epistasis adapt to a time budget, so profiling stays tractable on large landscapes such as GB1 or DHFR.

from graphfla import analysis

metrics = analysis.profile(landscape, seed=42)
metrics[["gamma", "fdc", "epistasis.magnitude"]]

analysis.profile(landscape, metrics=["ruggedness", "epistasis"])  # selected groups
analysis.local_optima_ratio(landscape)                            # a single metric
analysis.list_metrics()                                           # all available metrics

4. Mixed variable types

When columns differ in type, for example a categorical solvent alongside an ordinal temperature, use the general Landscape class and declare each column as "categorical", "ordinal" or "boolean".

import pandas as pd
from graphfla.landscape import Landscape

df = pd.read_csv("reactions.csv")
X = df[["solvent", "catalyst", "temperature"]]  # Reaction conditions
f = df["yield"]                                 # Product yield, to maximize

landscape = Landscape(maximize=True)
landscape.build_from_data(
    X, f, data_types={"solvent": "categorical", "catalyst": "categorical", "temperature": "ordinal"}
)

License

This project is licensed under the terms of the MIT License.

Citation

If you use GraphFLA, please cite:

@inproceedings{HuangZL25,
  author    = {Mingyu Huang and
               Shasha Zhou and
               Ke Li},
  title     = {Augmenting Biological Fitness Prediction Benchmarks with Landscape Features
               from GraphFLA},
  booktitle = {{NeurIPS}'25: Proc. of Advances in Neural Information Processing Systems 39},
  year      = {2025},
}
@article{HuangML25,
  author       = {Mingyu Huang and
                  Peili Mao and
                  Ke Li},
  title        = {Rethinking Performance Analysis for Configurable Software Systems:
                  {A} Case Study from a Fitness Landscape Perspective},
  journal      = {Proc. {ACM} Softw. Eng.},
  volume       = {2},
  number       = {{ISSTA}},
  pages        = {1748--1771},
  year         = {2025},
  doi          = {10.1145/3728954},
}
@inproceedings{HuangL25,
  author       = {Mingyu Huang and
                  Ke Li},
  title        = {On the Hyperparameter Loss Landscapes of Machine Learning Models:
                  An Exploratory Study},
  booktitle    = {{KDD}'25: Proceedings of the 31st {ACM} {SIGKDD} Conference on Knowledge Discovery
                  and Data Mining},
  pages        = {555--564},
  publisher    = {{ACM}},
  year         = {2025},
  doi          = {10.1145/3690624.3709229},
}

Happy analyzing! If you have questions or suggestions, please open an issue or start a discussion.