[docs]classOptimizer(Component):kernel_invariants={"actor","monitor","actor_name"}def__init__(self,actor_component:Parametrizable,actor_name:TStr,monitor_component:Measurable,monitor_name:TStr,*args,**kwargs):Component.__init__(self,*args,**kwargs)self.actor=actor_componentself.monitor=monitor_componentself.actor_name=actor_name# TODO: This is bit hacky since we modify the objects we act on. Better solutions welcome..replace_member(self.monitor,"_measure_for_optimizer",f"get_{monitor_name}")replace_member(self.actor,"_set_for_optimizer",f"set_{actor_name}")
[docs]defoptimize(self):raiseNotImplementedError("Implement the optimzer")
[docs]classBoundOptimizer(Optimizer):"""An Optimizer with restrictions on the values of the actor. Args: actor_min: The minumum value the actor is allowed to take during optimization actor_max: The maxiumum value the actor is allowed to take during optimization """kernel_invariants={"actor_min","actor_max"}def__init__(self,actor_min:TFloat,actor_max:TFloat,*args,**kwargs):Optimizer.__init__(self,*args,**kwargs)self.actor_min=actor_minself.actor_max=actor_max
[docs]classBisectionOptimizer(BoundOptimizer):"""An optimizer that uses bisection to achieve the optimization target on the monitor Args: timestep: The time the individual steps in the algorithm take epsilon: Acceptable relative difference from the target value (default 0.01, i.e. 1% of the target value) switch_actor: If True, the actor (needs to be Switchable) is switched on before the measurement of the monitor and switched off after (default False) """kernel_invariants={"timestep","epsilon","max_steps","switch_actor"}def__init__(self,timestep=325*us,epsilon=0.01,max_steps=20,switch_actor:TBool=False,*args,**kwargs):BoundOptimizer.__init__(self,*args,**kwargs)self.timestep=timestepself.epsilon=epsilonself.max_steps=max_steps@kerneldefdummy_function(self):self.experiment.log.warning(self.identifier+": cannot switch actor "+self.actor.identifier)self.switch_actor=switch_actorifnotisinstance(self.actor,Switchable):self._actor_on=dummy_function.__get__(self)self._actor_off=dummy_function.__get__(self)@kerneldef_actor_on(self):self.actor.on()@kerneldef_actor_off(self):self.actor.off()
[docs]@kernel(flags={"fast-math"})defoptimize(self,target:TFloat)->TFloat:soft_max=self.actor_maxsoft_min=self.actor_min# initial values for the bisectiontrial=(self.actor_max-self.actor_min)/2measurement=0.0# Do the bisection iterationforiinrange(self.max_steps):delay(self.timestep/2)# calculate and settrial=soft_min/2+soft_max/2iftrial<self.actor_min:trial=self.actor_miniftrial>self.actor_max:trial=self.actor_max# self.log.info(trial)self.actor._set_for_optimizer(trial)delay(self.timestep)# measureifself.switch_actor:self._actor_on()delay(self.timestep)measurement=self.monitor._measure_for_optimizer()delay(self.timestep)ifself.switch_actor:self._actor_off()self.experiment.log.debug("Bisection measurement result: {0}V ",[measurement])# comparedifference=measurement-targetifabs(difference/target)<self.epsilon:# self.log.info('eps')break# change borders for next iterationifdifference>0.0:soft_max=trialelse:soft_min=trialdelay(self.timestep/2)# delay to avoid collisionsdelay(0.03*us)returntrial