Trades API¶
Design of Experiments, batch evaluation, and Pareto analysis.
Overview¶
from phased_array_systems.trades import (
# Design Space
DesignSpace,
DesignVariable,
# DOE Generation
generate_doe,
# Batch Evaluation
BatchRunner,
# Pareto Analysis
filter_feasible,
extract_pareto,
rank_pareto,
)
# Additional functions via submodules
from phased_array_systems.trades.doe import generate_doe_from_dict, augment_doe
from phased_array_systems.trades.pareto import compute_hypervolume
Design Space¶
DesignSpace
¶
Bases: BaseModel
Collection of design variables defining a design space.
Provides methods for sampling the design space using various DOE methods (grid, random, LHS).
| ATTRIBUTE | DESCRIPTION |
|---|---|
variables |
List of design variables
TYPE:
|
name |
Optional name for the design space
TYPE:
|
add_variable
¶
add_variable(name: str, type: Literal['int', 'float', 'categorical'] = 'float', low: float | None = None, high: float | None = None, values: list[Any] | None = None) -> DesignSpace
Add a variable to the design space (fluent interface).
| PARAMETER | DESCRIPTION |
|---|---|
name
|
Variable name
TYPE:
|
type
|
Variable type
TYPE:
|
low
|
Lower bound
TYPE:
|
high
|
Upper bound
TYPE:
|
values
|
Categorical values
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DesignSpace
|
Self for chaining |
Source code in src/phased_array_systems/trades/design_space.py
sample
¶
sample(method: Literal['grid', 'random', 'lhs'] = 'lhs', n_samples: int = 100, seed: int | None = None, grid_levels: int | list[int] | None = None) -> DataFrame
Sample the design space.
| PARAMETER | DESCRIPTION |
|---|---|
method
|
Sampling method ("grid", "random", "lhs")
TYPE:
|
n_samples
|
Number of samples (ignored for grid method)
TYPE:
|
seed
|
Random seed for reproducibility
TYPE:
|
grid_levels
|
Number of levels per variable for grid method
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame with columns for each variable plus 'case_id' |
Source code in src/phased_array_systems/trades/design_space.py
get_variable
¶
get_variable(name: str) -> DesignVariable | None
DesignVariable
¶
Bases: BaseModel
Definition of a single design variable.
Supports continuous (float), discrete (int), and categorical variables.
| ATTRIBUTE | DESCRIPTION |
|---|---|
name |
Variable name, typically a dot-path like "array.nx"
TYPE:
|
type |
Variable type ("int", "float", or "categorical")
TYPE:
|
low |
Lower bound for continuous/discrete variables
TYPE:
|
high |
Upper bound for continuous/discrete variables
TYPE:
|
values |
List of allowed values for categorical variables
TYPE:
|
validate_bounds_or_values
¶
validate_bounds_or_values() -> DesignVariable
Ensure proper bounds/values are set based on type.
Source code in src/phased_array_systems/trades/design_space.py
sample_uniform
¶
Generate uniform random samples.
| PARAMETER | DESCRIPTION |
|---|---|
n
|
Number of samples
TYPE:
|
rng
|
NumPy random generator
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ndarray
|
Array of sampled values |
Source code in src/phased_array_systems/trades/design_space.py
scale_from_unit
¶
Scale values from [0, 1] to actual variable range.
| PARAMETER | DESCRIPTION |
|---|---|
unit_values
|
Values in [0, 1]
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ndarray
|
Scaled values in variable's actual range |
Source code in src/phased_array_systems/trades/design_space.py
get_grid_values
¶
Get grid values for this variable.
| PARAMETER | DESCRIPTION |
|---|---|
n_levels
|
Number of levels for grid
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[Any]
|
List of values at each level |
Source code in src/phased_array_systems/trades/design_space.py
DOE Generation¶
generate_doe
¶
generate_doe(design_space: DesignSpace, method: Literal['grid', 'random', 'lhs'] = 'lhs', n_samples: int = 100, seed: int | None = None, grid_levels: int | list[int] | None = None) -> DataFrame
Generate a Design of Experiments from a design space.
This is a convenience function that wraps DesignSpace.sample().
| PARAMETER | DESCRIPTION |
|---|---|
design_space
|
DesignSpace defining the variables and bounds
TYPE:
|
method
|
Sampling method - "grid": Full factorial grid (n_samples ignored) - "random": Uniform random sampling - "lhs": Latin Hypercube Sampling (space-filling)
TYPE:
|
n_samples
|
Number of samples (for random/lhs methods)
TYPE:
|
seed
|
Random seed for reproducibility
TYPE:
|
grid_levels
|
Number of levels per variable for grid method
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame with columns: - case_id: Unique identifier for each case - One column per design variable |
Examples:
>>> space = DesignSpace()
>>> space.add_variable("array.nx", "int", low=4, high=16)
>>> space.add_variable("array.ny", "int", low=4, high=16)
>>> space.add_variable("rf.tx_power_w_per_elem", "float", low=0.5, high=2.0)
>>> doe = generate_doe(space, method="lhs", n_samples=50, seed=42)
Source code in src/phased_array_systems/trades/doe.py
generate_doe_from_dict
¶
generate_doe_from_dict(variables: dict, method: Literal['grid', 'random', 'lhs'] = 'lhs', n_samples: int = 100, seed: int | None = None) -> DataFrame
Generate DOE from a simplified dictionary specification.
Convenience function for quick DOE generation without creating explicit DesignVariable objects.
| PARAMETER | DESCRIPTION |
|---|---|
variables
|
Dictionary mapping variable names to specs: - For continuous: {"name": (low, high)} or {"name": (low, high, "float")} - For discrete: {"name": (low, high, "int")} - For categorical: {"name": ["value1", "value2", ...]}
TYPE:
|
method
|
Sampling method
TYPE:
|
n_samples
|
Number of samples
TYPE:
|
seed
|
Random seed
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame with DOE cases |
Examples:
>>> doe = generate_doe_from_dict({
... "array.nx": (4, 16, "int"),
... "array.ny": (4, 16, "int"),
... "rf.tx_power_w_per_elem": (0.5, 2.0),
... "array.geometry": ["rectangular", "triangular"],
... }, n_samples=50)
Source code in src/phased_array_systems/trades/doe.py
augment_doe
¶
augment_doe(existing_doe: DataFrame, design_space: DesignSpace, n_additional: int, method: Literal['random', 'lhs'] = 'lhs', seed: int | None = None) -> DataFrame
Add additional samples to an existing DOE.
Useful for adaptive sampling or expanding a study.
| PARAMETER | DESCRIPTION |
|---|---|
existing_doe
|
Existing DOE DataFrame
TYPE:
|
design_space
|
DesignSpace defining the variables
TYPE:
|
n_additional
|
Number of additional samples to add
TYPE:
|
method
|
Sampling method for new samples
TYPE:
|
seed
|
Random seed
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
Combined DataFrame with original + new cases |
Source code in src/phased_array_systems/trades/doe.py
Batch Evaluation¶
BatchRunner
¶
BatchRunner(scenario: Scenario, requirements: RequirementSet | None = None, architecture_builder: Callable[[dict], Architecture] | None = None)
Parallel batch evaluation of DOE cases.
Evaluates multiple architecture/scenario combinations with case-level error handling, progress reporting, and resume capability.
| ATTRIBUTE | DESCRIPTION |
|---|---|
scenario |
Scenario to evaluate against
|
requirements |
Optional requirements for verification
|
architecture_builder |
Function to build Architecture from case dict
|
Initialize the batch runner.
| PARAMETER | DESCRIPTION |
|---|---|
scenario
|
Scenario to evaluate
TYPE:
|
requirements
|
Optional requirements for verification
TYPE:
|
architecture_builder
|
Function to convert case dict to Architecture. If None, uses default_architecture_builder.
TYPE:
|
Source code in src/phased_array_systems/trades/runner.py
run
¶
run(cases: DataFrame, n_workers: int = 1, cache_path: Path | None = None, progress_callback: Callable[[int, int], None] | None = None) -> DataFrame
Run batch evaluation.
| PARAMETER | DESCRIPTION |
|---|---|
cases
|
DataFrame with design variable columns + case_id
TYPE:
|
n_workers
|
Number of parallel workers (1 = sequential)
TYPE:
|
cache_path
|
Optional path to save/load partial results
TYPE:
|
progress_callback
|
Optional callback(completed, total) for progress
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame with all input columns + all metric columns |
Source code in src/phased_array_systems/trades/runner.py
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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
Pareto Analysis¶
filter_feasible
¶
filter_feasible(results: DataFrame, requirements: RequirementSet | None = None, verification_column: str = 'verification.passes') -> DataFrame
Filter results to only feasible (requirement-passing) designs.
| PARAMETER | DESCRIPTION |
|---|---|
results
|
DataFrame with evaluation results
TYPE:
|
requirements
|
Optional RequirementSet to verify against
TYPE:
|
verification_column
|
Column name for verification status
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame containing only feasible designs |
Source code in src/phased_array_systems/trades/pareto.py
extract_pareto
¶
extract_pareto(results: DataFrame, objectives: list[tuple[str, OptimizeDirection]], include_dominated: bool = False) -> DataFrame
Extract Pareto-optimal designs from results.
A design is Pareto-optimal if no other design is better in all objectives.
| PARAMETER | DESCRIPTION |
|---|---|
results
|
DataFrame with evaluation results
TYPE:
|
objectives
|
List of (column_name, direction) tuples where direction is "minimize" or "maximize"
TYPE:
|
include_dominated
|
If True, include a 'pareto_optimal' column marking Pareto-optimal rows
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame containing only Pareto-optimal designs (or all designs |
DataFrame
|
with pareto_optimal column if include_dominated=True) |
Examples:
>>> pareto = extract_pareto(results, [
... ("cost_usd", "minimize"),
... ("eirp_dbw", "maximize"),
... ])
Source code in src/phased_array_systems/trades/pareto.py
rank_pareto
¶
rank_pareto(pareto: DataFrame, objectives: list[tuple[str, OptimizeDirection]], weights: list[float] | None = None, method: Literal['weighted_sum', 'topsis'] = 'weighted_sum') -> DataFrame
Rank Pareto-optimal designs using weighted objectives.
| PARAMETER | DESCRIPTION |
|---|---|
pareto
|
DataFrame with Pareto-optimal designs
TYPE:
|
objectives
|
List of (column_name, direction) tuples
TYPE:
|
weights
|
Weights for each objective (default: equal weights)
TYPE:
|
method
|
Ranking method ("weighted_sum" or "topsis")
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame with added 'rank' and 'score' columns, sorted by rank |
Source code in src/phased_array_systems/trades/pareto.py
compute_hypervolume
¶
compute_hypervolume(pareto: DataFrame, objectives: list[tuple[str, OptimizeDirection]], reference_point: list[float] | None = None) -> float
Compute hypervolume indicator for a Pareto front.
The hypervolume is the volume of objective space dominated by the Pareto front, bounded by a reference point. Higher is better.
| PARAMETER | DESCRIPTION |
|---|---|
pareto
|
DataFrame with Pareto-optimal designs
TYPE:
|
objectives
|
List of (column_name, direction) tuples
TYPE:
|
reference_point
|
Reference point in objective space (default: worst point + 10%)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float
|
Hypervolume value |
Note
For >3 objectives, this uses a simple approximation.
Source code in src/phased_array_systems/trades/pareto.py
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | |
Usage Examples¶
Complete Trade Study¶
from phased_array_systems.trades import (
DesignSpace, generate_doe, BatchRunner,
filter_feasible, extract_pareto, rank_pareto
)
from phased_array_systems.requirements import Requirement, RequirementSet
# Define design space
space = (
DesignSpace(name="Array Trade")
.add_variable("array.nx", type="categorical", values=[4, 8, 16])
.add_variable("array.ny", type="categorical", values=[4, 8, 16])
.add_variable("rf.tx_power_w_per_elem", type="float", low=0.5, high=3.0)
# Fixed parameters
.add_variable("array.geometry", type="categorical", values=["rectangular"])
.add_variable("array.dx_lambda", type="float", low=0.5, high=0.5)
.add_variable("array.dy_lambda", type="float", low=0.5, high=0.5)
.add_variable("array.enforce_subarray_constraint", type="categorical", values=[True])
.add_variable("rf.pa_efficiency", type="float", low=0.3, high=0.3)
)
# Define requirements
requirements = RequirementSet(requirements=[
Requirement("REQ-001", "Min EIRP", "eirp_dbw", ">=", 35.0),
Requirement("REQ-002", "Max Cost", "cost_usd", "<=", 100000.0),
])
# Generate DOE
doe = generate_doe(space, method="lhs", n_samples=100, seed=42)
# Run batch evaluation
runner = BatchRunner(scenario, requirements)
results = runner.run(doe, n_workers=1)
# Filter and extract Pareto
feasible = filter_feasible(results, requirements)
objectives = [("cost_usd", "minimize"), ("eirp_dbw", "maximize")]
pareto = extract_pareto(feasible, objectives)
# Rank Pareto designs
ranked = rank_pareto(pareto, objectives, weights=[0.5, 0.5])
print(f"Top design: {ranked.iloc[0]['case_id']}")
Quick DOE¶
doe = generate_doe_from_dict(
{
"array.nx": (4, 16, "int"),
"array.ny": (4, 16, "int"),
"rf.tx_power_w_per_elem": (0.5, 3.0),
"array.geometry": ["rectangular", "triangular"],
},
n_samples=50,
seed=42,
)
Progress Callback¶
def show_progress(completed, total):
print(f"Progress: {completed}/{total} ({completed/total*100:.0f}%)")
results = runner.run(doe, progress_callback=show_progress)
Hypervolume¶
from phased_array_systems.trades import compute_hypervolume
hv = compute_hypervolume(pareto, objectives)
print(f"Hypervolume: {hv:.2e}")
Type Aliases¶
from phased_array_systems.types import OptimizeDirection
# OptimizeDirection = Literal["minimize", "maximize"]
objectives = [
("cost_usd", "minimize"), # Lower is better
("eirp_dbw", "maximize"), # Higher is better
]