-
-
Notifications
You must be signed in to change notification settings - Fork 121
Add bounded and inequality operand definition #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
f921ba2
5bfc9c1
f486688
539ed06
291740b
9dac12f
7f500d1
ae50cb7
f96209c
84d1abf
b5d8b14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
|
|
||
| Kramer Harrison, 2024 | ||
| """ | ||
| from dataclasses import dataclass | ||
| from optiland.optimization.operand.paraxial import ParaxialOperand | ||
| from optiland.optimization.operand.aberration import AberrationOperand | ||
| from optiland.optimization.operand.ray import RayOperand | ||
|
|
@@ -137,47 +138,88 @@ | |
| for name, func in METRIC_DICT.items(): | ||
| operand_registry.register(name, func) | ||
|
|
||
|
|
||
| class Operand(object): | ||
| @dataclass | ||
| class Operand: | ||
| """ | ||
| Represents an operand used in optimization calculations. | ||
| If no target is specified, a default is created at the current value. | ||
|
|
||
| Attributes: | ||
| type (str): The type of the operand. | ||
| target (float): The target value for the operand. | ||
| operand_type (str): The type of the operand. | ||
| target (float): The target value of the operand (equality operand). | ||
| min_val (float): The operand should stay above this value (inequality operand). | ||
| max_val (float): The operand should stay below this value (inequality operand). | ||
| weight (float): The weight of the operand. | ||
| input_data (dict): Additional input data for the operand's metric | ||
| function. | ||
| input_data (dict): Additional input data for the operand. | ||
|
|
||
| Methods: | ||
| value(): Get the current value of the operand. | ||
| delta(): Calculate the difference between the target and current value. | ||
| delta_target(): Calculate the difference between the value and target. | ||
| delta_ineq(): Calculate the difference between the value and targets. | ||
| fun(): Calculate the objective function value. | ||
| """ | ||
|
|
||
| def __init__(self, operand_type, target, weight, input_data={}): | ||
| self.type = operand_type | ||
| self.target = target | ||
| self.weight = weight | ||
| self.input_data = input_data | ||
| operand_type: str = None | ||
| target: float = None | ||
| min_val: float = None | ||
| max_val: float = None | ||
| weight: float = None | ||
| input_data: dict = None | ||
|
|
||
| def __post_init__(self): | ||
| if self.min_val is not None and self.max_val is not None and self.min_val > self.max_val: | ||
| raise ValueError(f"{self.operand_type} operand: min_val is higher than max_val") | ||
| if self.target is not None and (self.min_val is not None or self.max_val is not None): | ||
| raise ValueError(f"{self.operand_type} operand cannot accept both equality and inequality targets") | ||
| if all(x is None for x in [self.target, self.min_val, self.max_val]): | ||
| self.target = self.value # No target has been defined, default it to the current value | ||
|
|
||
| @property | ||
| def value(self): | ||
| """Get current value of the operand""" | ||
| metric_function = operand_registry.get(self.type) | ||
| metric_function = operand_registry.get(self.operand_type) | ||
| if metric_function: | ||
| return metric_function(**self.input_data) | ||
| else: | ||
| raise ValueError(f'Unknown operand type: {self.type}') | ||
| raise ValueError(f'Unknown operand type: {self.operand_type}') | ||
|
|
||
| def delta(self): | ||
| """Calculate the difference between the target and current value""" | ||
| return (self.value - self.target) | ||
| def delta_target(self): | ||
| """Calculate the difference between the value and target""" | ||
| return self.value - self.target | ||
|
|
||
| def delta_ineq(self): | ||
| """Calculate the difference between the value and targets. | ||
|
|
||
| If the value is on the right side of the bound(s), | ||
| then this operand simply is zero. | ||
| """ | ||
|
|
||
| # One of the two | ||
|
||
| if self.min_val is not None and not self.max_val: | ||
| return 0 if self.value > self.min_val else self.min_val-self.value | ||
|
|
||
| # One of the two | ||
| if self.max_val is not None and not self.min_val: | ||
| return 0 if self.value < self.max_val else self.value-self.max_val | ||
|
|
||
| # Both of them | ||
| if self.max_val is not None and self.min_val is not None: | ||
| distance_to_closest_target = min(abs(self.value-self.min_val), abs(self.value-self.max_val)) | ||
| return 0 if (self.value > self.min_val and self.value < self.max_val) else distance_to_closest_target | ||
|
|
||
| def delta(self): | ||
| """Calculate the difference to target""" | ||
| if self.target is not None: | ||
| return self.delta_target() | ||
| elif self.min_val is not None or self.max_val is not None: | ||
| return self.delta_ineq() | ||
| else: | ||
| raise ValueError(f"{self.operand_type} operand cannot compute delta") | ||
|
|
||
| def fun(self): | ||
| """Calculate the objective function value""" | ||
| return self.weight * self.delta() | ||
|
|
||
| def __str__(self): | ||
| """Return a string representation of the operand""" | ||
| return self.type.replace('_', ' ') | ||
| return self.operand_type.replace('_', ' ') | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These details are not critical at all, but consider:
tolto get a better final result e.g.,res = optimizer.optimize(tol=1e-9)