Observed probability \varepsilon _test of constraint violation for i=10. Distribution is based on 500 trials for each value of \varepsilon . Dashed line shows the outcome predicted by formula \eqref {eq:tempoformula}, i.e., \varepsilon _test = \varepsilon .
# Stochastic constraint tightening for a scalar system.# [makes] sampling.matimportnumpyasnpimportscipy.iofromscipy.linalgimportsolve_discrete_areimportmatplotlib.pyplotaspltfromscipy.statsimportnorm,uniformimportos# Stochastic constraint tightening for a scalar system.# [makes] sampling.matA=np.array([[1]])B=np.array([[1]])Q=np.array([[0.5]])R=np.array([[1]])# Compute LQR gainP=solve_discrete_are(A,B,Q,R)K=-np.linalg.inv(R+B.T@P@B)@(B.T@P@A)K=K[0,0]# Convert to scalarAK=A[0,0]+B[0,0]*KU=np.array([-1,1])X=np.array([-1,1])W=np.array([-0.5,0.5])N=10# Number of time points over which to consider disturbanceSKinf=W/(1-AK)Ubar=U-abs(K)*SKinfXbar_robust=X-SKinfdefevolution(A,B,Nt):"""Computes the state evolution matrix for N steps."""A=np.atleast_2d(A)B=np.atleast_2d(B)Nx=B.shape[0]Nu=B.shape[1]E=np.zeros((Nt*Nx,Nt*Nu))e=np.hstack([B,np.zeros((Nx,(Nt-1)*Nu))])forkinrange(Nt):i=slice(k*Nx,(k+1)*Nx)E[i,:]=eifk<Nt-1:e=A@ej=slice((k+1)*Nu,(k+2)*Nu)e[:,j]=BreturnEE=evolution(AK,1,N)# Converts from vector of w(k) to vector of e(k)np.random.seed(0)defsampleE(n):returnE@(W[0]+(W[1]-W[0])*np.random.rand(N,n))# Reduce number of trials and samples for performanceNtrials=500beta=0.01epsilons=np.logspace(-3,0,16)Ms=np.ceil(2./epsilons*(2-np.log(beta))).astype(int)esim=sampleE(1000000).reshape(-1,1000000)Xbar=np.full((2,len(Ms),Ntrials),np.nan)eviolation=np.full((len(Ms),Ntrials),np.nan)foriinrange(len(Ms)):print(f'{i:2d}. epsilon = {epsilons[i]:10g} (M = {Ms[i]})')forjinrange(Ntrials):e=sampleE(Ms[i]).reshape(-1,Ms[i])emin=min(np.min(e[0,:]),0)emax=max(np.max(e[0,:]),0)eviolation[i,j]=np.mean(esim[0,:]<emin)+np.mean(esim[0,:]>emax)Xbar[0,i,j]=min(X[0]-emin,0)Xbar[1,i,j]=max(X[1]-emax,0)defviolationquantiles(x,v,logscale=True):"""Plots quantiles of violation probability."""quantiles=np.array([0.05,0.25,0.5,0.75,0.95])colors=plt.cm.jet(np.linspace(0,1,len(quantiles)))q=np.quantile(np.squeeze(v),quantiles,axis=1).Tifnotos.getenv('OMIT_PLOTS')=='true':iflogscale:q=np.maximum(q,1e-16)plotfunc=plt.loglogelse:plotfunc=plt.semilogxplt.figure()forjinrange(len(quantiles)):plotfunc(x,q[:,j],'-o',color=colors[j],label=f'percentile {100*quantiles[j]:.0f}')plt.legend()returnqifnotos.getenv('OMIT_PLOTS')=='true':# Plot tightened setslabels=['Xbar_{lb}','Xbar_{ub}']locations=['lower left','upper left']foriinrange(2):plt.figure()q=violationquantiles(epsilons,Xbar[i,:,:],False)plt.plot(epsilons[[0,-1]],Xbar_robust[i]*np.ones(2),'-k',label='Robust')plt.xlabel('ε')plt.ylabel(labels[i])plt.legend(loc=locations[i])plt.title('Tightened Constraints')plt.axis([min(epsilons),max(epsilons),i-2.1,i-0.9])# Plot violation probabilitiesplt.figure()q=violationquantiles(epsilons,eviolation,True)plt.plot(epsilons,epsilons,'--k')plt.legend(loc='lower right')plt.ylabel('ε_{test}')plt.xlabel('ε')plt.axis([min(epsilons),max(epsilons),1e-6,1])plt.show()# Save datadata={'Ms':Ms,'Xbar':Xbar,'Xbar_robust':Xbar_robust,'eviolation':eviolation,'beta':beta,'epsilons':epsilons}scipy.io.savemat('sampling.mat',data)