Core Base API
Extracted documentation from src/mdotoolbox/core/base.py.
DoE
Attributes
x (Union[list, np.ndarray]): Input samples as a matrix (n_samples x n_vars) or vector (for 1D problems). Each row represents one sample point. y (Union[dict, list, np.ndarray]): Output evaluations. Can be: - dict: Multiple outputs with named keys {name: array of values} - list/array: Single output (converted to dict with key 'y') constraint_violation (Union[np.ndarray, None]): Optional array of constraint violation values for each sample. None indicates no constraints or all points are feasible. Default is None.
Examples
Single output
>>> doe = DoE(x=[[1, 2], [3, 4]], y=[0.5, 1.2]) >>> >>> # Multiple outputs with constraints >>> doe = DoE( ... x=[[1, 2], [3, 4]], ... y={'obj': [0.5, 1.2], 'c1': [0.1, -0.2]}, ... constraint_violation=[0.0, 0.2] ... )
Raises: TypeError: If x or y are not of the correct type. ValueError: If dimensions of x, y, and constraint_violation don't match.
update_DoE
Examples
doe = DoE(x=[[1, 2]], y=[0.5]) >>> doe.update_DoE(x_n=[3, 4], y_n=1.2) >>> print(len(doe.x)) # Now has 2 samples 2
to_dict
Examples
doe = DoE(x=[[1, 2]], y={'obj': [0.5]}, constraint_violation=[0.1]) >>> data = doe.to_dict() >>> print(data.keys()) dict_keys(['x', 'obj', 'constraint_violation'])
get_feasible_indices
Examples
doe = DoE(x=[[1], [2], [3]], y=[1, 2, 3], ... constraint_violation=[0, 0.1, 0.0001]) >>> feasible_idx = doe.get_feasible_indices(tol=0.01) >>> print(feasible_idx) # Points 0 and 2 [0 2]
get_best_feasible
Examples
doe = DoE( ... x=[[1], [2], [3]], ... y={'obj': [10, 5, 8]}, ... constraint_violation=[0.5, 0.0, 0.1] ... ) >>> x_best, f_best, cv_best = doe.get_best_feasible(tol=0.2) >>> print(f"Best: x={x_best}, f={f_best}, cv={cv_best}") Best: x=[3], f=8, cv=0.1
get_f_min
Examples
doe = DoE(x=[[1], [2]], y={'obj': [10, 5]}) >>> f_min = doe.get_f_min() >>> print(f_min) 5
Function
Attributes
func (Callable): The actual function to evaluate. Should accept individual arguments matching the variable names in x. x (Union[str, List[str], np.ndarray[str]]): Variable names that the function depends on. Can be a single variable name or a list of names. name (str, optional): Descriptive name for the function (e.g., "drag", "lift", "constraint_1"). Defaults to empty string.
Examples
Simple quadratic function
>>> def quad(x, y): ... return x**2 + y**2 >>> f = Function(func=quad, x=['x', 'y'], name='quadratic') >>> >>> # Constraint function >>> def constraint(x): ... return x - 5 >>> c = Function(func=constraint, x='x', name='lower_bound')
Raises: TypeError: If func is not callable, x is not string-like, or name is not a string.
Notes
The function is called with positional arguments in the order specified by x. For example, if x=['a', 'b', 'c'], func will be called as func(a_val, b_val, c_val).
Constraint
Attributes
func (Function): The constraint function wrapped in a Function object. ctype (str, optional): Constraint type. Must be one of: - 'ge': Greater than or equal (func(x) >= value) - 'le': Less than or equal (func(x) <= value) - 'eq': Equal to (func(x) = value) Defaults to 'ge'. value (Union[int, float], optional): Right-hand side value of the constraint. Defaults to 0.0.
Examples
Box constraint: x >= 0
>>> def x_func(x): ... return x >>> f = Function(func=x_func, x='x', name='x_value') >>> c = Constraint(func=f, ctype='ge', value=0.0) >>> >>> # Nonlinear constraint: x^2 + y^2 <= 1 (unit circle) >>> def circle(x, y): ... return x**2 + y**2 >>> f = Function(func=circle, x=['x', 'y'], name='circle') >>> c = Constraint(func=f, ctype='le', value=1.0)
Raises: TypeError: If func is not a Function object, ctype is not a string, or value is not numeric. ValueError: If ctype is not one of 'ge', 'le', or 'eq'.
Notes
- Internally, constraints are converted to the form func(x) OP value
- For inequality constraints, violation is measured as the amount by which the constraint is not satisfied
Problem
Attributes
objective (Function): Objective function to minimize (or maximize if maximize=True). constraints (Iterable): Collection of Constraint objects defining the feasible region. Can be empty for unconstrained problems. ubounds (ArrayLike): Upper bounds for each variable. Must have same length as objective.x. Use np.inf for unbounded variables. lbounds (ArrayLike): Lower bounds for each variable. Must have same length as objective.x. Use -np.inf for unbounded variables. maximize (bool, optional): If True, maximize the objective instead of minimizing. Internally converted to minimization. Defaults to False. tol (float, optional): Tolerance for numerical comparisons and convergence checks. Defaults to 1e-6. name (str, optional): Descriptive name for the problem. Defaults to "".
Examples
Unconstrained problem: minimize (x-2)^2
>>> def obj(x): ... return (x - 2)**2 >>> f = Function(func=obj, x='x', name='quadratic') >>> problem = Problem( ... objective=f, ... constraints=[], ... lbounds=[-10], ... ubounds=[10], ... name='simple_quadratic' ... ) >>> >>> # Constrained problem: minimize f(x,y) subject to x+y >= 1 >>> def obj(x, y): ... return x**2 + y**2 >>> def con(x, y): ... return x + y >>> f = Function(func=obj, x=['x', 'y']) >>> c = Constraint(func=Function(func=con, x=['x', 'y']), ctype='ge', value=1.0) >>> problem = Problem( ... objective=f, ... constraints=[c], ... lbounds=[0, 0], ... ubounds=[10, 10] ... )
Raises: TypeError: If inputs are not of the correct type. ValueError: If bounds length doesn't match number of variables or constraint variables don't match objective variables.
Notes
- If maximize=True, the objective is internally negated for minimization
- Bounds are stored as self.bounds (n_vars x 2) array
- Constraint names are auto-generated if not provided
evaluate
Examples
Evaluate objective only
>>> f_val, _ = problem.evaluate(x=[1.0, 2.0], f=True, c=False) >>> >>> # Evaluate constraints only >>> _, c_vals = problem.evaluate(x=[1.0, 2.0], f=False, c=True) >>> >>> # Evaluate both >>> f_val, c_vals = problem.evaluate(x=[1.0, 2.0], f=True, c=True)
Notes
The objective is called with unpacked x values as positional arguments.
initial_DoE
Examples
Fixed number of samples
>>> doe = problem.initial_DoE(n=20) >>> >>> # Adaptive number based on problem dimension >>> doe = problem.initial_DoE(n=lambda n: 3*n + 1)
Notes
- For infinite bounds, uses +/-100 or +/-(bound + 50) as limits
- Evaluates objective as 'obj' and constraints as 'c{i}-{type}-{value}'
- Automatically computes constraint violations
compute_constraint_violation
Examples
For constraints c1(x) >= 0 and c2(x) <= 5
>>> constraint_vals = { ... 'c0-ge-0.0': np.array([1.0, -0.5, 0.1]), # c1 values ... 'c1-le-5.0': np.array([3.0, 6.0, 4.9]) # c2 values ... } >>> violations = problem.compute_constraint_violation(constraint_vals) >>> # violations = [0, 0.25+1.0, 0] = [0, 1.25, 0]
Notes
- For 'ge' constraints: violation = max(0, value - c_val)^2
- For 'le' constraints: violation = max(0, c_val - value)^2
- For 'eq' constraints: violation = |c_val - value|^2
- Total violation is the sum across all constraints
ParetoEntry
Attributes
f: Objective value. h: Total constraint violation h_total. J_i: Total coupling discrepancy J_i. z_bar: Shared design variables at this iterate. x_bar: Local design variables at this iterate. y_bar: Coupling variables at this iterate. code: Integer in {1,...,7}. Bit 1 set if non-dominated in (f,h); bit 2 if non-dominated in (f,J); bit 4 if non-dominated in (h,J).
BestSolution
Attributes
z_bar: Shared design variables at the best iterate. x_bar: Local design variables at the best iterate. y_bar: Coupling variables at the best iterate. f: Objective value at the best iterate. h: Running minimum of h_total (used in strict improvement check). J_i: Coupling discrepancy J_i at the best iterate. h_history: All observed h_total values in order of observation.
Results
Attributes
best: Best iterate found under the lexicographic criterion. converged: True if termination criteria were met. iterations: Number of system-level iterations performed. evaluations: Total number of discipline evaluations performed. elapsed_time: Wall-clock time in seconds. history: DataFrame with one row per iteration. pareto: Pareto set at termination. DataFrame with columns [f_sys, J_i, h_total, z_bar, x_bar, y_bar, code]. code is an integer bitmask (1-7) encoding membership in bi-objective spaces (f,h), (f,J), (h,J).
BudgetManager
Attributes
mode (Literal["shared", "weighted", "fixed"]): Budget allocation strategy: - "shared": Single shared pool, first-come-first-served - "weighted": Proportional split based on ratios - "fixed": User-specified exact allocations Defaults to "weighted". total_budget (int, optional): Total evaluation budget. Required for "shared" and "weighted" modes. system_ratio (float, optional): Fraction of budget for system-level (weighted mode only). Must be in (0, 1). Defaults to 0.5. subsystem_weights (List[float], optional): Relative weights for each subsystem (weighted mode). Must sum to ~1.0. subsystem_budgets (List[int], optional): Exact budgets for each subsystem (fixed mode). Required if mode="fixed". system_budget (int, optional): Exact budget for system-level (fixed mode). Required if mode="fixed". iteration_ratio (float, optional): Fraction of allocated budget to use per iteration (0, 1). If None, use full budget per iteration. Defaults to None.
Examples
Mode 1: Shared pool (400 total, use until exhausted)
>>> budget = BudgetManager(mode="shared", total_budget=400) >>> >>> # Mode 2: Weighted split (50% system, 50% subsystems) >>> budget = BudgetManager( ... mode="weighted", ... total_budget=400, ... system_ratio=0.5, ... subsystem_weights=[0.3, 0.5, 0.2] ... ) >>> >>> # Mode 3: Weighted with iteration ratio (5% per iteration) >>> budget = BudgetManager( ... mode="weighted", ... total_budget=400, ... system_ratio=0.5, ... subsystem_weights=[0.3, 0.5, 0.2], ... iteration_ratio=0.05 ... ) >>> >>> # Mode 4: Fixed budgets with iteration ratio >>> budget = BudgetManager( ... mode="fixed", ... subsystem_budgets=[100, 70], ... system_budget=120, ... iteration_ratio=0.05 ... )
Notes
- Call initialize(n_subsystems) before using the budget manager
- Use get_subsystem_max_iter() and get_system_max_iter() to get available evaluations for each call
- Record actual usage with record_subsystem_evals() and record_system_evals()
- Check exhaustion with is_subsystem_exhausted(), is_system_exhausted(), is_total_exhausted()
Raises: ValueError: If mode is invalid, weights don't sum to 1, or required parameters are missing for the selected mode.