Skip to content

Base generator class

Generator

Bases: XoptBaseModel, Generator, ABC

Base class for Generators.

Generators are responsible for generating new points to evaluate.

Attributes:

Name Type Description
name str

Name of the generator.

supports_batch_generation bool

Flag that describes if this generator can generate batches of points.

supports_multi_objective bool

Flag that describes if this generator can solve multi-objective problems.

vocs VOCS

Generator VOCS.

data (DataFrame, optional)

Generator data.

model_config ConfigDict

Model configuration.

Source code in xopt/generator.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
class Generator(XoptBaseModel, BaseGenerator, ABC):
    """
    Base class for Generators.

    Generators are responsible for generating new points to evaluate.

    Attributes
    ----------
    name : str
        Name of the generator.
    supports_batch_generation : bool
        Flag that describes if this generator can generate batches of points.
    supports_multi_objective : bool
        Flag that describes if this generator can solve multi-objective problems.
    vocs : VOCS
        Generator VOCS.
    data : pd.DataFrame, optional
        Generator data.
    model_config : ConfigDict
        Model configuration.
    """

    name: ClassVar[str] = Field(description="generator name")

    supports_batch_generation: bool = Field(
        default=False,
        description="flag that describes if this "
        "generator can generate "
        "batches of points",
        frozen=True,
        exclude=True,
    )
    supports_multi_objective: bool = Field(
        default=False,
        description="flag that describes if this generator can solve multi-objective "
        "problems",
        frozen=True,
        exclude=True,
    )
    supports_single_objective: bool = Field(
        default=False,
        description="flag that describes if this generator can solve single-objective "
        "problems",
        frozen=True,
        exclude=True,
    )
    supports_constraints: bool = Field(
        default=False,
        description="flag that describes if this generator can solve "
        "constrained optimization problems",
        frozen=True,
        exclude=True,
    )

    vocs: VOCS = Field(description="generator VOCS")
    data: Optional[pd.DataFrame] = Field(
        None, description="generator data", exclude=True
    )

    model_config = ConfigDict(validate_assignment=True)

    @field_validator("vocs", mode="after")
    def validate_vocs(cls, v, info: ValidationInfo):
        if v.n_constraints > 0 and not info.data["supports_constraints"]:
            raise VOCSError("this generator does not support constraints")

        # assert that the generator needs at least one objective
        if v.n_objectives == 0:
            raise VOCSError("the generator must have at least one objective")

        if v.n_objectives == 1:
            if not info.data["supports_single_objective"]:
                raise VOCSError(
                    "this generator does not support single objective optimization"
                )
        elif v.n_objectives > 1 and not info.data["supports_multi_objective"]:
            raise VOCSError(
                "this generator does not support multi-objective optimization"
            )

        return v

    @field_validator("data", mode="before")
    def validate_data(cls, v):
        if isinstance(v, dict):
            try:
                v = pd.DataFrame(v)
            except Exception as e:
                # Pydantic catches this first
                if isinstance(
                    e, ValueError
                ) and "If using all scalar values, you must pass an index" in str(e):
                    v = pd.DataFrame(v, index=[0])
                else:
                    raise
        return v

    def _validate_vocs(self, vocs: VOCS):
        pass

    def __init__(self, **kwargs):
        """
        Initialize the generator.
        """
        super().__init__(**kwargs)
        logger.info(f"Initialized generator {self.name}")

    def suggest(self, num_points: Optional[int]) -> List[dict]:
        return self.generate(num_points)

    def ingest(self, results: List[dict]) -> None:
        self.add_data(pd.DataFrame(results))

    @property
    def is_done(self):
        return self._is_done

    @abstractmethod
    def generate(self, n_candidates: int) -> list[dict[Hashable, Any]]:
        pass

    def add_data(self, new_data: pd.DataFrame):
        """
        update dataframe with results from new evaluations.

        This is intended for generators that maintain their own data.

        """
        if self.data is not None:
            self.data = pd.concat([self.data, new_data], axis=0, ignore_index=True)
        else:
            self.data = new_data

    def model_dump(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
        """overwrite model dump to remove faux class attrs"""

        res = super().model_dump(*args, **kwargs)

        res.pop("supports_batch_generation", None)
        res.pop("supports_multi_objective", None)

        return res

__init__(**kwargs)

Initialize the generator.

Source code in xopt/generator.py
119
120
121
122
123
124
def __init__(self, **kwargs):
    """
    Initialize the generator.
    """
    super().__init__(**kwargs)
    logger.info(f"Initialized generator {self.name}")

add_data(new_data)

update dataframe with results from new evaluations.

This is intended for generators that maintain their own data.

Source code in xopt/generator.py
140
141
142
143
144
145
146
147
148
149
150
def add_data(self, new_data: pd.DataFrame):
    """
    update dataframe with results from new evaluations.

    This is intended for generators that maintain their own data.

    """
    if self.data is not None:
        self.data = pd.concat([self.data, new_data], axis=0, ignore_index=True)
    else:
        self.data = new_data

model_dump(*args, **kwargs)

overwrite model dump to remove faux class attrs

Source code in xopt/generator.py
152
153
154
155
156
157
158
159
160
def model_dump(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
    """overwrite model dump to remove faux class attrs"""

    res = super().model_dump(*args, **kwargs)

    res.pop("supports_batch_generation", None)
    res.pop("supports_multi_objective", None)

    return res

yaml(**kwargs)

serialize first then dump to yaml string

Source code in xopt/pydantic.py
231
232
233
234
235
236
237
238
def yaml(self, **kwargs):
    """serialize first then dump to yaml string"""
    output = json.loads(
        self.to_json(
            **kwargs,
        )
    )
    return yaml.dump(output)

StateOwner

Mix-in class that represents a generator that owns its own state and needs special handling of data loading on deserialization.

Source code in xopt/generator.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
class StateOwner:
    """
    Mix-in class that represents a generator that owns its own state and needs special handling
    of data loading on deserialization.
    """

    def set_data(self, data: pd.DataFrame):
        """
        Set the full dataset for the generator. Typically only used when loading from a save file.

        Parameters
        ----------
        data : pd.DataFrame
            The data to set.
        """
        raise NotImplementedError

set_data(data)

Set the full dataset for the generator. Typically only used when loading from a save file.

Parameters:

Name Type Description Default
data DataFrame

The data to set.

required
Source code in xopt/generator.py
169
170
171
172
173
174
175
176
177
178
def set_data(self, data: pd.DataFrame):
    """
    Set the full dataset for the generator. Typically only used when loading from a save file.

    Parameters
    ----------
    data : pd.DataFrame
        The data to set.
    """
    raise NotImplementedError