IdeaSearch LogoIdeaSearch

README

README of IdeaSearch-fit

GitHubIdeaSearch/IdeaSearch-fit

42

IdeaSearch-fit

Quick Start

To install IdeaSearch-fit, run:

pip install IdeaSearch-fit

Project Overview

IdeaSearch-fit is a symbolic-regression application for the IdeaSearch framework. It provides IdeaSearchFitter, a Helper object that configures formula generation, numerical parameter fitting, evaluation, and result reporting before being attached to an IdeaSearcher with bind_helper().

Scope

Use this package to generate and compare candidate expressions for data X and targets y, including experiments on how semantic context or IdeaSearch search settings affect the result. The package reports numerical fit quality and expression complexity; it does not establish physical interpretation, causal validity, robustness, or extrapolation. Validate selected expressions with held-out data and domain-specific checks.

Configuration Model

StageMain configuration or interfaceResult
Datadata, data_path, result_pathInput arrays and output directory
Semantic contextvariable_names, output_name, units, descriptions, auto_polishPrompt context for candidate generation
Expression grammarfunctions, constant_whitelist, constant_mapAllowed symbols in executable expressions
Generation modegenerate_fuzzy, fuzzy_translatorDirect expressions or hypothesis-to-expression translation
Numerical evaluationoptimization_method, optimization_trial_num, metric-mapping settingsFitted parameters and candidate scores
Agent loopIdeaSearcher.bind_helper(), model, memory, island, migration, and budget settingsIterative candidate generation and evaluation
Result accessget_best_fit(), get_pareto_frontier()Best numerical fit and accuracy–complexity trade-off

To compare search configurations, keep the data, expression grammar, metric, numerical optimizer, model version, and random settings fixed unless they are the variables being tested.

Processing Pipeline

IdeaSearch-fit evaluates a candidate in two stages:

  1. Candidate structure generation: the LLM receives the configured data summary, variable metadata, optional domain descriptions, and expression grammar. It returns an expression directly, or a natural-language hypothesis followed by an expression when generate_fuzzy=True.
  2. Parameter fitting and scoring: the expression is parsed, its free parameters are fitted with L-BFGS-B or differential-evolution, and the configured metric is mapped to an IdeaSearch score.

Only expressions that pass parsing and enabled validation checks enter the reported results.

Core Input and Output

Core Input:

  • X: Input data (independent variables, multi-dimensional support).
  • y: Target data (dependent variable, one-dimensional).
  • error (Optional): Measurement uncertainty for each value in y. When supplied, it is used by the uncertainty-weighted fit metric; otherwise the fitter uses mean squared error.

Core Output:

  • A Pareto frontier containing the retained accuracy–complexity trade-off, together with access to the current best numerical fit.

Key Features

  • Evolutionary Symbolic Regression: Uses the multi-island evolution framework of IdeaSearch to explore candidate mathematical expressions.

  • Pareto Frontier: Reports retained candidates across the configured accuracy–complexity trade-off.

  • Dimensional Consistency Checks: Checks physical dimensions when unit validation is enabled. Dimensional consistency is a constraint, not proof of physical correctness.

  • Dual-Mode Formula Generation:

    • Precise Generation: Directly generates and evolves mathematical expressions that strictly adhere to a specific computational syntax.
    • Fuzzy Generation: First, an LLM proposes a natural-language hypothesis about patterns in the data. Another LLM agent then translates the hypothesis into a strict mathematical expression.
  • Automated Semantic Polishing: The system can invoke an LLM to propose descriptions of the inputs and outputs for the search prompt.

  • Seamless Integration and Automation: As a highly integrated module for IdeaSearch, the IdeaSearchFitter class only needs to be initialized once. It then automatically configures all the necessary core components for IdeaSearch, including the evaluation function (evaluate_func), prompts (prologue_section, epilogue_section), and mutation/crossover operators (mutation_func, crossover_func).

Core API

The primary interface for the IdeaSearch-fit package is the IdeaSearchFitter class, which consists of its constructor and several result-retrieval methods.

  • IdeaSearchFitter(__init__): ⭐️ Important

    • Function: Initializes an IdeaSearchFitter instance. This is the unified entry point for all configurations, including data loading, problem definition, expression construction, and search strategy settings.
    • Importance: The first step in using IdeaSearch-fit, defining the entire framework for the symbolic regression task.
  • get_best_fit():

    • Function: Returns the numerical expression string, fitted with its optimal parameters, that achieved the lowest metric value (e.g., mean squared error) among all evaluated formulas.
    • Importance: Retrieves the single best fitting result based on accuracy alone.
  • get_pareto_frontier():

    • Function: Returns a dictionary containing all formulas on the current Pareto frontier, along with their detailed information (complexity, metric value, fitted parameters, etc.).
    • Importance: Retrieves a set of optimal solutions that balance accuracy and complexity.

Configuration Parameters

All configurations for an IdeaSearchFitter instance are set in its __init__ constructor. The parameters are logically grouped by function to facilitate understanding and setup.

1. Task Input & Output

  • data: Optional[Dict[str, ndarray]]
    • Data passed directly as a dictionary in memory. Must contain keys "x" (input, 2D array) and "y" (output, 1D array). Can optionally include "error" (errors for y, 1D array).
  • data_path: Optional[str]
    • Path to a local .npz file from which to load data. Use either data or data_path. The file should contain arrays with the same keys.
  • result_path: str
    • Path to an existing directory where fitting results, such as the Pareto frontier report (pareto_report.txt) and data (pareto_data.json), will be stored.

2. Problem Definition & Units

  • variable_names: Optional[List[str]]
    • A list of names for the input variables (e.g., ["mass", "velocity"]). Defaults to ["x1", "x2", ...] if not provided.
  • output_name: Optional[str]
    • The name of the output variable (e.g., "energy").
  • perform_unit_validation: bool (Default: False)
    • Whether to enable dimensional consistency checks.
  • variable_units: Optional[List[str]]
    • A list of units corresponding to variable_names (e.g., ["kg", "m s^-1"]). Required when perform_unit_validation is True.
  • output_unit: Optional[str]
    • The unit of the output variable (e.g., "J" or "kg m^2 s^-2"). Required when perform_unit_validation is True.
  • auto_polish: bool (Default: True)
    • Whether to invoke an LLM to propose variable and output descriptions for the search prompt.
  • auto_polisher: Optional[str]
    • The name of the LLM model to use for the auto_polish task. If None, an available model is selected automatically.
  • input_description, variable_descriptions, output_description: Optional[str]
    • Manually specify text descriptions for the system, input variables, and output variable. If provided, the auto_polish step will be skipped.

3. Expression Building Blocks

  • functions: List[str] (Default: Includes common functions like sin, cos, log, exp)
    • A list of mathematical functions allowed in expressions. All functions must be supported by the numexpr library.
  • constant_whitelist: List[str] (Default: [])
    • A list of named constants (as strings) allowed in expressions.
  • constant_map: Dict[str, float] (Default: {"pi": np.pi})
    • A dictionary mapping named constants to their float values.

4. Search & Evolution Strategy

  • LLM Generation Strategy
    • generate_fuzzy: bool (Default: True)
      • [Master Switch] Enables "fuzzy generation" mode. If False, the LLM will generate strict mathematical expressions directly.
    • fuzzy_translator: Optional[str]
      • In fuzzy mode, the name of the LLM model used to translate natural language theories into mathematical expressions.
  • Numerical Optimization Strategy
    • optimization_method: Literal["L-BFGS-B", "differential-evolution"] (Default: "L-BFGS-B")
      • Specifies the numerical optimization algorithm for fitting ansatz parameters. L-BFGS-B is an efficient local optimizer suitable for smoother objective functions. differential-evolution is a global optimizer that may be more robust for complex problems with multiple local minima.
    • optimization_trial_num: int (Default: 100)
      • The number of trials for the numerical optimizer. For differential-evolution, this corresponds to the population size. For non-convex or complex problems, increasing this value can improve the probability of finding the global optimum at the cost of increased computation time.
  • Evolutionary Operators (Still work in progress)
    • enable_mutation: bool (Default: False)
      • Enables the built-in expression mutation functionality.
    • enable_crossover: bool (Default: False)
      • Enables the built-in expression crossover functionality.

5. Score Mapping

  • baseline_metric_value: Optional[float]
    • Defines a "baseline" metric value (e.g., MSE) that will be mapped to an IdeaSearch score of 20.0. If None, the result of a naive linear fit is used as the baseline.
  • good_metric_value: Optional[float]
    • Defines a "good" metric value that will be mapped to an IdeaSearch score of 80.0.
  • metric_mapping: Literal["linear", "logarithm"] (Default: "linear")
    • The mapping function from metric value to score. Logarithmic mapping is more suitable when metric values can span several orders of magnitude.

6. Miscellaneous

  • seed: Optional[int]
    • Seed for the fitter's local random-number generator. Repeating a complete run also requires fixed model versions, API settings, and IdeaSearch configuration.

Workflow

The following is a complete workflow example (from https://github.com/IdeaSearch/IdeaSearch-fit-test) that demonstrates how to solve a symbolic regression problem using IdeaSearch-fit and IdeaSearch.

# pip install IdeaSearch-fit
import numpy as np
from IdeaSearch import IdeaSearcher
from IdeaSearch_fit import IdeaSearchFitter


def build_data():
    """
    Builds simulated data for fitting.
    True Formula: x = 1.2*A + A*exp(-0.7*gamma*t) - A*exp(-0.5*gamma*t)*cos(3*omega*t)
    """
    n_samples = 1000
    rng = np.random.default_rng(seed=42)

    # Define the value ranges for the independent variables
    A_range = (1.0, 10.0)
    gamma_range = (0.1, 1.0)
    omega_range = (1.0, 5.0)
    t_range = (0.0, 10.0)

    # Sample randomly within the defined ranges
    A_samples = rng.uniform(A_range[0], A_range[1], n_samples)
    gamma_samples = rng.uniform(gamma_range[0], gamma_range[1], n_samples)
    omega_samples = rng.uniform(omega_range[0], omega_range[1], n_samples)
    t_samples = rng.uniform(t_range[0], t_range[1], n_samples)

    # Stack the independent variables into the format required by the Fitter (n_samples, n_features)
    x_data = np.stack([A_samples, gamma_samples, omega_samples, t_samples], axis=1)

    # Calculate the y values based on the true formula and add noise
    y_true = (1.2 * A_samples +
              A_samples * np.exp(-0.7 * gamma_samples * t_samples) -
              A_samples * np.exp(-0.5 * gamma_samples * t_samples) * np.cos(3 * omega_samples * t_samples))
    error_data = 0.01 + 0.02 * np.abs(y_true)
    y_data = y_true + rng.normal(0, error_data)

    # Note: This example does not pass error_data to the Fitter, so MSE will be used as the metric.
    return {
        "x": x_data,
        "y": y_data,
    }


def main():
    # 1. Prepare the data
    data = build_data()

    # 2. Initialize IdeaSearchFitter
    fitter = IdeaSearchFitter(
        result_path = "fit_results", # Ensure this directory exists
        data = data,
        variable_names = ["A", "gamma", "omega", "t"],
        variable_units = ["m", "s^-1", "s^-1", "s"],
        output_name = "x",
        output_unit = "m",
        constant_whitelist = ["1", "2", "pi"],
        constant_map = {"1": 1, "2": 2, "pi": np.pi},
        auto_polish = True,
        generate_fuzzy = True,
        perform_unit_validation = True,
        optimization_method = "L-BFGS-B",
        optimization_trial_num = 5,
    )

    # 3. Initialize IdeaSearcher
    ideasearcher = IdeaSearcher()

    # 4. Basic Configuration
    ideasearcher.set_program_name("IdeaSearch Fitter Test")
    ideasearcher.set_database_path("database") # Ensure this directory exists
    ideasearcher.set_api_keys_path("api_keys.json")
    ideasearcher.set_models(["gemini-2.5-pro"]) # Use your desired model

    ideasearcher.set_record_prompt_in_diary(True) # Optional: Record prompts in the diary log

    # 5. Bind the Fitter! (The most crucial step)
    ideasearcher.bind_helper(fitter)

    # 6. Define and execute the evolutionary loop
    island_num = 3
    cycle_num = 3
    unit_interaction_num = 20

    for _ in range(island_num):
        ideasearcher.add_island()

    for cycle in range(cycle_num):
        print(f"---[ Cycle {cycle + 1}/{cycle_num} ]---")
        if cycle != 0: ideasearcher.repopulate_islands()
        ideasearcher.run(unit_interaction_num)
        print("Done.")

    # 7. Retrieve and display the results
    print("\n---[ Search Complete ]---")
    print(f"Best Fit Formula: {fitter.get_best_fit()}")

    print("\nOptimal Solutions on the Pareto Frontier:")
    pareto_frontier = fitter.get_pareto_frontier()
    if not pareto_frontier:
        print("  - No solutions found on the Pareto frontier.")
    else:
        for complexity, info in sorted(pareto_frontier.items()):
            metric_key = "reduced chi squared" if "reduced chi squared" in info else "mean square error"
            metric_value = info.get(metric_key, float('nan'))
            print(f"  - Complexity: {complexity}, Error: {metric_value:.4g}, Formula: {info.get('ansatz', 'N/A')}")


if __name__ == "__main__":
    # Before running, please ensure:
    # 1. The './fit_results' and './database' directories exist.
    # 2. The 'api_keys.json' file is correctly configured.
    main()
Edit on GitHub

Last updated on

On this page