Source code for statease.optimizer

from enum import Enum
import json

[docs] class Goal(Enum): """An enumeration representing the different types of goals a Criteria can have.""" NONE = 0 MAXIMIZE = 1 MINIMIZE = 2 TARGET = 3 IN_RANGE = 4 EQUAL_TO = 5 CPK = 6 def __str__(self): return self.name
[docs] class Optimizer: """The Optimizer class is used to set criteria on one or more Analyses, then use those criteria to find the optimal parameters. """ def __init__(self, client): self.__client = client result = self.__client.send_payload({ "method": "GET", "uri": "optimizer", }) self.__criteria = [] self.__solutions = tuple() def __str__(self): out = '' for c in self.__criteria: if c.goal != Goal.NONE: out += '{}\n'.format(c) out += "Found {} solutions:".format(len(self.__solutions)) for s in self.__solutions: out += '{}\n'.format(s) return out def have_criteria(self): for c in self.__criteria: if c.factor and c.goal == Goal.NONE: return False return True def add_criteria(self, criteria): if criteria.factor and criteria.goal == Goal.NONE: raise ValueError("Can't add criteria - no goal specified for '{}'.".format(criteria.name)) if criteria.restrict_discrete==True: if not criteria.factor: raise ValueError("Can't add criteria - It is not a discrete factor. For response/analysis '{}', restrict_discrete must be False.".format(criteria.name)) if (criteria.factor.type != "Numeric") or (criteria.factor.type == "Numeric" and criteria.factor.subtype != "Discrete"): raise ValueError("Can't add criteria - It is not a discrete factor. For factor '{}', restrict_discrete must be False.".format(criteria.name)) self.__criteria.append(criteria) @property def solutions(self): return self.__solutions
[docs] def optimize(self): """ Runs the optimization routine. Must have one or more Criteria specified. """ if not self.have_criteria(): raise ValueError("Can't run optimization - no criteria specified!") result = self.__client.send_payload({ "method": "POST", "uri": "optimizer", "criteria": [ c.to_dict() for c in self.__criteria ], }) solutions = [] for solution in result['payload']['solutions']: solutions.append(json.loads(solution)) self.__solutions = tuple(solutions)
[docs] class Criteria: """The Criteria class is used by the optimizer to calculate a desirability score for a given point in the design space, which is then used to search for an optimal point. Each Analysis and Factor can have a Criteria (e.g. you might maximize the output of an Analysis, and target a certain value of a Factor). """ def __init__(self, factor=None, analysis=None): """ Create a Criteria for a Factor or Analysis. """ if (not analysis and not factor) or (analysis and factor): raise ValueError("You must pass in either an analysis or factor.") self.__analysis = analysis self.__factor = factor self.__name = '' self.__goal = Goal.NONE self.__target = 0 self.__lower_limit = 0 self.__upper_limit = 0 self.__lower_weight = 1 self.__upper_weight = 1 self.__importance = 0 self.__restrict_discrete = False def __str__(self): name = '' if self.__analysis: name += self.__analysis.name if self.__factor: name += self.__factor.name result = "Criteria for {} -> Goal: {} Target: {} Lower Limit: {} Upper Limit: {} Lower Weight: {} Upper Weight: {} Importance: {} Restrict discrete: {}".format( name, self.__goal, self.__target, self.__lower_limit, self.__upper_limit, self.__lower_weight, self.__upper_weight, self.__importance, self.__restrict_discrete, ) if self.__factor: result += " " + "Restrict discrete: "+ str(self.__restrict_discrete) return result @property def name(self): """ The name of this Criteria (analysis/response or factor name). """ if self.__analysis: self.__name = self.__analysis.name elif self.__factor: self.__name = self.__factor.name else: raise ValueError("You must pass in either an analysis or factor.") return self.__name @property def goal(self): """ The goal of this Criteria (e.g. Goal.MAXIMIZE). """ return self.__goal @goal.setter def goal(self, goal): self.__goal = goal @property def target(self): """ The target for this Criteria, if using Goal.EQUAL_TO or Goal.TARGET """ return self.__target @target.setter def target(self, target): self.__target = target @property def lower_limit(self): """ The lower limit for this Criteria. """ return self.__lower_limit @lower_limit.setter def lower_limit(self, lower_limit): self.__lower_limit = lower_limit @property def upper_limit(self): """ The upper limit for this Criteria. """ return self.__upper_limit @upper_limit.setter def upper_limit(self, upper_limit): self.__upper_limit = upper_limit @property def lower_weight(self): """ The lower weight for this Criteria. """ return self.__lower_weight @lower_weight.setter def lower_weight(self, lower_weight): self.__lower_weight = lower_weight @property def upper_weight(self): """ The upper weight for this Criteria. """ return self.__upper_weight @upper_weight.setter def upper_weight(self, upper_weight): self.__upper_weight = upper_weight @property def importance(self): """ The importance of this Criteria, relative to other Criteria. """ return self.__importance @importance.setter def importance(self, importance): self.__importance = importance @property def restrict_discrete(self): """ The restrict discrete for this Criteria. """ return self.__restrict_discrete @property def factor(self): return self.__factor @restrict_discrete.setter def restrict_discrete(self, restrict_discrete): self.__restrict_discrete = restrict_discrete def to_dict(self): out_dict = { 'analysis': self.__analysis.name if self.__analysis else None, 'factor': self.__factor.name if self.__factor else None, 'goal': str(self.__goal), } # the rest of the members are primitives for k, v in self.__dict__.items(): key = k[11:] # strip off leading 'Criteria__' if key in out_dict.keys(): continue out_dict[key] = v return out_dict