# Delaying the evaluation of a test function. 

from pyopus.optimizer.base import RandomDelay
from pyopus.problems.mgh import Rosenbrock
from platform import system
if system()=='Windows':
	# perf_counter() is the most precise timer in Windows
	from time import perf_counter as timer
else:
	# time() is the most precise timer in Linux
	from time import time as timer

def myfunc(x):
	return 2.0*x
	
if __name__=='__main__':
	callf=myfunc
	xini=10.0
	
	# Delay is chonen randomly from [1.0, 2.0] with uniform distribution. 
	delayedCallable=RandomDelay(callf, [1.0, 2.0])
	
	print("Starting evaluation of myfunc() without delay.")
	t1=timer()
	f=callf(xini)
	dt=timer()-t1
	print(f"Evaluation took {dt:.3f}s, f={f:e}")
	
	print("\nStarting evaluation of myfunc() with delay.")
	t1=timer()
	f=delayedCallable(xini)
	dt=timer()-t1
	print(f"Evaluation took {dt:.3f}s, f={f:e}")
	
	# Calling a test suite function evaluates both f and g. To delay
	# the call to f() or g() method, use wrapper objects. 
	rosenbrockObject=Rosenbrock()
	xini=rosenbrockObject.initial
	# Delaying function evaluation
	delayedF=RandomDelay(rosenbrockObject.cpi()['f'], [1.0, 2.0])
	print("\n\nStarting Rosenbrock f evaluation with delay.")
	t1=timer()
	f=delayedF(xini)
	dt=timer()-t1
	print(f"Evaluation took {dt:.3f}s, f={f:e}")
	
	# Delaying gradient evaluation
	delayedG=RandomDelay(rosenbrockObject.cpi()['g'], [1.0, 2.0])
	print("\nStarting Rosenbrock g evaluation with delay.")
	t1=timer()
	g=delayedG(xini)
	dt=timer()-t1
	print(f"Evaluation took {dt:.3f}s, g={str(g)}")
	