from typing import Any
import numpy as np
from foxes_opt.core.farm_objective import FarmObjective
from foxes_opt.core.farm_opt_problem import FarmOptProblem
import foxes.constants as FC
[docs]
class MaxNTurbines(FarmObjective):
"""
Maximizes the number of turrbines.
"""
def __init__(
self,
problem: FarmOptProblem,
name: str = "max_n_turbines",
check_valid: bool = True,
**kwargs: Any,
) -> None:
"""
Parameters
----------
problem
The underlying optimization problem
name
The name of the objective function
check_valid
Check FC.VALID variable before counting
kwargs
Additional parameters for `FarmObjective`
"""
super().__init__(problem, name, **kwargs)
self.check_valid = check_valid
[docs]
def n_components(self) -> int:
"""
Returns the number of components of the
function.
Returns
-------
value
The number of components.
"""
return 1
[docs]
def maximize(self) -> list[bool]:
"""
Returns flag for maximization of each component.
Returns
-------
flags
Bool array for component maximization,
shape
"""
return [True]
[docs]
def calc_individual(
self,
vars_int: np.ndarray,
vars_float: np.ndarray,
problem_results: Any,
components: list[int] | None = None,
) -> np.ndarray:
"""
Calculate values for a single individual of the
underlying problem.
Parameters
----------
vars_int
The integer variable values, shape: (n_vars_int,)
vars_float
The float variable values, shape: (n_vars_float,)
problem_results
The results of the variable application
to the problem
components
The selected components or None for all
Returns
-------
values
The component values, shape: (n_sel_components,)
"""
if FC.VALID in problem_results and self.check_valid:
vld = np.sum(problem_results[FC.VALID].to_numpy(), axis=1)
if np.min(vld) != np.max(vld):
raise ValueError(
f"Objective '{self.name}': Number of valid turbines is state dependend, counting impossible"
)
return np.array([vld[0]], dtype=np.float64)
else:
return np.array([self.farm.n_turbines], dtype=np.float64)
[docs]
def calc_population(
self,
vars_int: np.ndarray,
vars_float: np.ndarray,
problem_results: Any,
components: list[int] | None = None,
) -> np.ndarray:
"""
Calculate values for all individuals of a population.
Parameters
----------
vars_int
The integer variable values, shape: (n_pop, n_vars_int)
vars_float
The float variable values, shape: (n_pop, n_vars_float)
problem_results
The results of the variable application
to the problem
components
The selected components or None for all
Returns
-------
values
The component values, shape: (n_pop, n_sel_components)
"""
n_pop = problem_results["n_pop"].to_numpy()
if self.check_valid:
n_states = problem_results["n_org_states"].to_numpy()
n_turbines = self.farm.n_turbines
vld = (
problem_results[FC.VALID]
.to_numpy()
.reshape(n_states, n_pop, n_turbines)
)
vld = np.swapaxes(vld, 0, 1)
vld = np.sum(vld, axis=2)
if np.any(np.min(vld, axis=1) != np.max(vld, axis=1)):
raise ValueError(
f"Objective '{self.name}': Number of valid turbines is state dependend, counting impossible"
)
return vld[:, 0, None]
else:
return np.full((n_pop, 1), self.farm.n_turbines, dtype=vars_float.dtype)