{"id":25850593,"url":"https://github.com/kch3782/torcwa","last_synced_at":"2025-04-09T20:04:09.581Z","repository":{"id":57680357,"uuid":"486407126","full_name":"kch3782/torcwa","owner":"kch3782","description":"GPU-accelerated RCWA with automatic differentiation","archived":false,"fork":false,"pushed_at":"2024-06-15T22:54:15.000Z","size":4361,"stargazers_count":123,"open_issues_count":12,"forks_count":24,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-09T20:04:04.772Z","etag":null,"topics":["electromagnetic-simulation","optics-simulation","pytorch","rigorous-coupled-wave"],"latest_commit_sha":null,"homepage":"","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kch3782.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-28T01:34:53.000Z","updated_at":"2025-04-07T16:58:22.000Z","dependencies_parsed_at":"2025-03-01T12:36:06.892Z","dependency_job_id":null,"html_url":"https://github.com/kch3782/torcwa","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kch3782%2Ftorcwa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kch3782%2Ftorcwa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kch3782%2Ftorcwa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kch3782%2Ftorcwa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kch3782","download_url":"https://codeload.github.com/kch3782/torcwa/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103865,"owners_count":21048245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["electromagnetic-simulation","optics-simulation","pytorch","rigorous-coupled-wave"],"created_at":"2025-03-01T12:24:36.308Z","updated_at":"2025-04-09T20:04:09.550Z","avatar_url":"https://github.com/kch3782.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"**torcwa-0.1.4.2**\n======\n\n* License: LGPL\n\n* User guide: [Link](./docs/TORCWA_User_Guide_ver_0.1.0.pdf)\n\n\u003cbr/\u003e\n\nFeatures\n--------\n**torcwa** (**torc**h + **rcwa**) is a PyTorch implementation of rigorous coupled-wave analysis (RCWA)\n\n* **GPU-accelerated** simulation\n\n* Supporting **automatic differentiation** for optimization\n\n* Units: Lorentz-Heaviside units\n\n\t* Speed of light: 1\n\n\t* Permittivity and permeability of vacuum: both 1\n\n* Notation: exp(-*jωt*)\n\n\u003cbr/\u003e\n\nCitation\n--------\n```\n@article{\n\ttitle = {TORCWA: GPU-accelerated Fourier modal method and gradient-based optimization for metasurface design},\n\tjournal = {Computer Physics Communications},\n\tvolume = {282},\n\tpages = {108552},\n\tyear = {2023},\n\tdoi = {https://doi.org/10.1016/j.cpc.2022.108552},\n\tauthor = {Changhyun Kim and Byoungho Lee},\n}\n```\n\n\u003cbr/\u003e\n\nInstallation\n------------\n* Requirements\n\n\t* Python version 3.8 or higher\n\n\t* PyTorch version 1.10.1 or higher\n\n\t* For GPU operation, GPUs that support CUDA operations\n\n* After installing the above requirement, run the following command at the command prompt.\n```\n$ pip install torcwa\n```\n* If the PyTorch version is lower than the required, it will automatically install PyTorch 1.10.1 or higher, but the CPU-only PyTorch or incompatible version may be installed. Therefore, **before installing using the above command, please install PyTorch version that is compatible with GPU**.\n\n\u003cbr/\u003e\n\nUpdated features in 0.1.4\n------------\n1. For S-parameters and sources, p-pol. and s-pol. notation is also available.\n   - There may be unexpected bugs. If S-paramter is greatly different when comparing with other simulations, or if there are other bugs, please report on issue tap or contact us.\n\n2. When calculating the gradient of eigendecomposition, the default value of the broadening parameter (related to stabilization) is changed to 10^-10.\n\n3. Class 'geometry' is added and have grid number and lattice constant as instance variables.\n   - 'rcwa_geo' uses class variables and class methods, and will be removed in a future version.\n\n4. Minor bugs are fixed in version 0.1.4.2\n\n\n\u003cbr/\u003e\n\nTORCWA Examples\n---------------\n1. [Example 0](./example/Example0.ipynb): Fresnel equation\n\n2. [Example 1](./example/Example1.ipynb): Simulation with rectangular meta-atom  \nNormal incidence / Parametric sweep on wavelength / View electromagnetic field\n\n3. [Example 1-1](./example/Example1-1.ipynb): Simulation with stacked meta-atom  \nNormal incidence / View electromagnetic field\n\n4. [Example 2](./example/Example2.ipynb): Simulation with square meta-atom  \nOblique incidence / View electromagnetic field\n\n5. [Example 3](./example/Example3.ipynb): Simulation with rectangular meta-atom  \nNormal incidence / Parametric sweep on geometric parameters\n\n6. [Example 4](./example/Example4.ipynb): Gradient calculation of cylindrical meta-atom  \nDifferentiation of transmittance with respect to radius\n\n7. [Example 5](./example/Example5.ipynb): Shape optimization  \nMaximize anisotropy\n\n8. [Example 6](./example/Example6.ipynb): Topology optimization  \nMaximize 1st order diffraction\n\n\u003cbr/\u003e\n\nSimulation - Example 1\n----------------------------\n![schematic](./image/schematic.png)\n![ex1_schematic](./image/example1_schematic.png)\n\n\u003cbr/\u003e\n\n**1.** Define simulation parameters\n```python\nimport numpy as np\nimport torch\nfrom matplotlib import pyplot as plt\nimport scipy.io\n\nimport torcwa\nimport Materials\n\n# Hardware\n# If GPU support TF32 tensor core, the matmul operation is faster than FP32 but with less precision.\n# If you need accurate operation, you have to disable the flag below.\ntorch.backends.cuda.matmul.allow_tf32 = False\nsim_dtype = torch.complex64\ngeo_dtype = torch.float32\ndevice = torch.device('cuda')\n\n# Simulation environment\n# light\ninc_ang = 0.*(np.pi/180)    # radian\nazi_ang = 0.*(np.pi/180)    # radian\n\n# material\nsubstrate_eps = 1.46**2\n\n# geometry\nL = [300., 300.]            # nm / nm\ntorcwa.rcwa_geo.Lx = L[0]\ntorcwa.rcwa_geo.Ly = L[1]\ntorcwa.rcwa_geo.nx = 300\ntorcwa.rcwa_geo.ny = 300\ntorcwa.rcwa_geo.grid()\ntorcwa.rcwa_geo.edge_sharpness = 1000.\ntorcwa.rcwa_geo.dtype = geo_dtype\ntorcwa.rcwa_geo.device = device\nz = torch.linspace(-500,1500,501,device=device)\n\nx_axis = torcwa.rcwa_geo.x.cpu()\ny_axis = torcwa.rcwa_geo.y.cpu()\nz_axis = z.cpu()\n\n# layers\nlayer0_geometry = torcwa.rcwa_geo.rectangle(Wx=180.,Wy=100.,Cx=L[0]/2.,Cy=L[1]/2.)\nlayer0_thickness = 300.\n```\n\n* Settings\n\n\t* Only PyTorch is required to run the simulation, but other additional libraries are required for data plotting and saving. (Here, matplotlib and scipy are utilized.)\n\n\t* torch.backends.cuda.matmul.allow_tf32  \n\t**RTX 3090 or later models** support TF32 core operation for matrix multiplication. This is faster than the conventional computation with less accuracy. It is recommended to set to False for accurate operation.\n\n\t* sim_dtype  \n\tThis is a data type that requires **complex number operation** and is used when declaring simulation.\n\n\t* geo_dtype  \n\tThis is a data type that requires **real number operation** and is used when declaring geometric parameters, wavelength, and incident angles.\n\n\t* torcwa.rcwa_geo  \n\tIf the lattice constant and sampling number are specified, basic geometry such as rectangle and circle and functions such as union and intersection can be used. The generated geometry is expressed as 1 or 0 on the grid. The edge sharpness of the geometry also can be specified. The higher this value, the sharper the edge.\n\n\u003cbr/\u003e\n\n* Variables\n\t* inc_ang: incident angle (*θi* in above image)\n\t* azi_ang: azimuthal angle of incidence (*θa* in above image)\n\t* substrate_eps: permittivity of substrate\n\t* L: Lattice constant ([Tx, Ty] in above image)\n\t* layer0_geometry: rectangle with Wx = 180, Wy = 100\n\t* layer0_thickness: height of structure (h in above image)\n\n\u003cbr/\u003e\n\n**2.** View internal layer geometry\n```python\n# View layers\nplt.imshow(torch.transpose(layer0_geometry,-2,-1).cpu(),origin='lower',extent=[x_axis[0],x_axis[-1],y_axis[0],y_axis[-1]])\nplt.title('Layer 0')\nplt.xlim([0,L[0]])\nplt.xlabel('x (nm)')\nplt.ylim([0,L[1]])\nplt.ylabel('y (nm)')\nplt.colorbar()\n```\n\n\u003cbr/\u003e\n\n**3.** Generate and perform simulation (**Only get S-paramters** without electromagnetic field)\n```python\norder_N = 15\norder = [order_N,order_N]\nlamb0 = torch.linspace(400.,700.,61,dtype=geo_dtype,device=device)\n\ntxx = []\nfor lamb0_ind in range(len(lamb0)):\n    lamb0_now = lamb0[lamb0_ind]\n\t# Declare simulation\n    sim = torcwa.rcwa(freq=1/lamb0_now,order=order,L=L,dtype=sim_dtype,device=device)\n\t# Add input and output layer (This step can be skipped if both layers are free space)\n    sim.add_input_layer(eps=substrate_eps)\n\t# Set incident angle\n    sim.set_incident_angle(inc_ang=inc_ang,azi_ang=azi_ang)\n\t# Add internal layer\n    silicon_eps = Materials.aSiH.apply(lamb0_now)**2\n    layer0_eps = layer0_geometry*silicon_eps + (1.-layer0_geometry)\n    sim.add_layer(thickness=layer0_thickness,eps=layer0_eps)\n\t# Solve global S-matrix\n    sim.solve_global_smatrix()\n\t# Get S-parameters\n    txx.append(sim.S_parameters(orders=[0,0],direction='forward',port='transmission',polarization='xx',ref_order=[0,0]))\ntxx = torch.cat(txx)\n```\n\n* Variables\n\t* order: truncated Fourier order [x-direction, y-direction]\n\t* lamb0: wavelength for parametric sweep\n\n\u003cbr/\u003e\n\n* Sequence\n\n\t1. Declare simulation\n\t\t* freq: Frequency\n\t\t* order: Truncated Fourier order\n\t\t* L: Lattice constant\n\t\t* dtype: Simulation data type\n\t\t* device: Simulation device\n\n\t2. Add input and output layer (This step can be skipped if both layers are free space)\n\n\t3. Set incident angle\n\t\t* inc_ang: Incident angle\n\t\t* azi_ang: Azimuthal angle of incidence\n\t\t* angle_layer: Reference layer to incident and azimuthal angle (default:'input')\n\t\n\t4. Add internal layer\n\n\t5. Solve global S-matrix\n\n\t6. Get S-parameters\n\t\t* orders\n\t\t* direction (forward/backward)\n\t\t* port (transmission/reflection)\n\t\t* polarization (xx/xy/yx/yy)\n\t\t* ref_order: Reference order to calculate S-paramters\n\n\u003cbr/\u003e\n\n**4.** View spectrum and export data\n```python\nplt.plot(lamb0.cpu(),torch.abs(txx).cpu()**2)\nplt.title('Spectrum (order: '+str(order_N)+')')\nplt.xlabel('Wavelength (nm)')\nplt.ylabel('Transmittance (a.u.)')\nplt.grid()\n\nex1_data = {'lamb0':lamb0.cpu().numpy(),'txx':txx.cpu().numpy()}\nscipy.io.savemat('Example1_spectrum_data_order_'+str(order_N)+'.mat',ex1_data)\n```\n\n\u003cbr/\u003e\n\n**5.** Generate and perform simulation (Get electromagnetic field)\n```python\nlamb0 = torch.tensor(532.,dtype=geo_dtype,device=device)    # nm\n\norder_N = 15\norder = [order_N,order_N]\nsim = torcwa.rcwa(freq=1/lamb0,order=order,L=L,dtype=sim_dtype,device=device)\nsim.add_input_layer(eps=substrate_eps)\nsim.set_incident_angle(inc_ang=inc_ang,azi_ang=azi_ang)\nsilicon_eps = Materials.aSiH.apply(lamb0)**2\nlayer0_eps = layer0_geometry*silicon_eps + (1.-layer0_geometry)\nsim.add_layer(thickness=layer0_thickness,eps=layer0_eps)\nsim.solve_global_smatrix()\n# Set light source\nsim.source_planewave(amplitude=[1.,0.],direction='forward')\n\n# Get electromagnetic field\n[Ex, Ey, Ez], [Hx, Hy, Hz] = sim.field_xz(torcwa.rcwa_geo.x,z,L[1]/2)\nEnorm = torch.sqrt(torch.abs(Ex)**2 + torch.abs(Ey)**2 + torch.abs(Ez)**2)\nHnorm = torch.sqrt(torch.abs(Hx)**2 + torch.abs(Hy)**2 + torch.abs(Hz)**2)\n```\n\n* Sequence\n\n\t7. Set light source\n\t\t* amplitude\n\t\t* direction (forward/backward)\n\n\t8. Get electromagnetic field\n\t\t* x, y, z axis or point\n\n\u003cbr/\u003e\n\n**6.** View electromagnetic field and export data\n```python\nfig, axes = plt.subplots(figsize=(10,12),nrows=2,ncols=4)\nim0 = axes[0,0].imshow(torch.transpose(Enorm,-2,-1).cpu(),cmap='jet',origin='lower',extent=[x_axis[0],x_axis[-1],z_axis[0],z_axis[-1]])\naxes[0,0].set(title='E norm',xlim=(0,L[0]),xlabel='x (nm)',ylim=(z_axis[0],z_axis[-1]),ylabel='z (nm)')\nim1 = axes[0,1].imshow(torch.transpose(torch.abs(Ex),-2,-1).cpu(),cmap='jet',origin='lower',extent=[x_axis[0],x_axis[-1],z_axis[0],z_axis[-1]])\naxes[0,1].set(title='Ex abs',xlim=(0,L[0]),xlabel='x (nm)',ylim=(z_axis[0],z_axis[-1]),ylabel='z (nm)')\nim2 = axes[0,2].imshow(torch.transpose(torch.abs(Ey),-2,-1).cpu(),cmap='jet',origin='lower',extent=[x_axis[0],x_axis[-1],z_axis[0],z_axis[-1]])\naxes[0,2].set(title='Ey abs',xlim=(0,L[0]),xlabel='x (nm)',ylim=(z_axis[0],z_axis[-1]),ylabel='z (nm)')\nim3 = axes[0,3].imshow(torch.transpose(torch.abs(Ez),-2,-1).cpu(),cmap='jet',origin='lower',extent=[x_axis[0],x_axis[-1],z_axis[0],z_axis[-1]])\naxes[0,3].set(title='Ez abs',xlim=(0,L[0]),xlabel='x (nm)',ylim=(z_axis[0],z_axis[-1]),ylabel='z (nm)')\nim4 = axes[1,0].imshow(torch.transpose(Hnorm,-2,-1).cpu(),cmap='jet',origin='lower',extent=[x_axis[0],x_axis[-1],z_axis[0],z_axis[-1]])\naxes[1,0].set(title='H norm',xlim=(0,L[0]),xlabel='x (nm)',ylim=(z_axis[0],z_axis[-1]),ylabel='z (nm)')\nim5 = axes[1,1].imshow(torch.transpose(torch.abs(Hx),-2,-1).cpu(),cmap='jet',origin='lower',extent=[x_axis[0],x_axis[-1],z_axis[0],z_axis[-1]])\naxes[1,1].set(title='Hx abs',xlim=(0,L[0]),xlabel='x (nm)',ylim=(z_axis[0],z_axis[-1]),ylabel='z (nm)')\nim6 = axes[1,2].imshow(torch.transpose(torch.abs(Hy),-2,-1).cpu(),cmap='jet',origin='lower',extent=[x_axis[0],x_axis[-1],z_axis[0],z_axis[-1]])\naxes[1,2].set(title='Hy abs',xlim=(0,L[0]),xlabel='x (nm)',ylim=(z_axis[0],z_axis[-1]),ylabel='z (nm)')\nim7 = axes[1,3].imshow(torch.transpose(torch.abs(Hz),-2,-1).cpu(),cmap='jet',origin='lower',extent=[x_axis[0],x_axis[-1],z_axis[0],z_axis[-1]])\naxes[1,3].set(title='Hz abs',xlim=(0,L[0]),xlabel='x (nm)',ylim=(z_axis[0],z_axis[-1]),ylabel='z (nm)')\nfig.colorbar(im0,ax=axes[0,0])\nfig.colorbar(im1,ax=axes[0,1])\nfig.colorbar(im2,ax=axes[0,2])\nfig.colorbar(im3,ax=axes[0,3])\nfig.colorbar(im4,ax=axes[1,0])\nfig.colorbar(im5,ax=axes[1,1])\nfig.colorbar(im6,ax=axes[1,2])\nfig.colorbar(im7,ax=axes[1,3])\n\nex1_XZ_data = {'x_axis':x_axis.numpy(),'y_axis':y_axis.numpy(),'z_axis':z_axis.numpy(),\\\n    'Ex':Ex.cpu().numpy(),'Ey':Ey.cpu().numpy(),'Ez':Ez.cpu().numpy(),'Enorm':Enorm.cpu().numpy(),\\\n    'Hx':Hx.cpu().numpy(),'Hy':Hy.cpu().numpy(),'Hz':Hz.cpu().numpy(),'Hnorm':Hnorm.cpu().numpy()}\nscipy.io.savemat('Example1_XZ_data.mat',ex1_XZ_data)\n```\n\n\u003cbr/\u003e\n\nOptimization - Example 6\n------------\n![ex6_schematic](./image/example6_schematic.png)\n\n**1.** Define simulation parameters\n```python\nimport numpy as np\nimport torch\nimport scipy.io\nfrom matplotlib import pyplot as plt\nimport time\n\nimport torcwa\nimport Materials\n\n# Hardware\nsim_dtype = torch.complex64\ngeo_dtype = torch.float32\ndevice = torch.device('cuda')\n\n# Simulation environment\n# light\nlamb0 = torch.tensor(532.,dtype=geo_dtype,device=device)    # nm\ninc_ang = 0.*(np.pi/180)    # radian\nazi_ang = 0.*(np.pi/180)    # radian\n\n# material\nsubstrate_eps = 1.46**2\nsilicon_eps = Materials.aSiH.apply(lamb0)**2\n\n# geometry\nL = [700., 300.]            # nm / nm\ntorcwa.rcwa_geo.Lx = L[0]\ntorcwa.rcwa_geo.Ly = L[1]\ntorcwa.rcwa_geo.nx = 700\ntorcwa.rcwa_geo.ny = 300\ntorcwa.rcwa_geo.grid()\ntorcwa.rcwa_geo.edge_sharpness = 1000.\ntorcwa.rcwa_geo.dtype = geo_dtype\ntorcwa.rcwa_geo.device = device\n\nx_axis = torcwa.rcwa_geo.x.cpu()\ny_axis = torcwa.rcwa_geo.y.cpu()\n\n# layers\nlayer0_thickness = 300.\n```\n\n* Same as simulation example\n\n\u003cbr/\u003e\n\n**2.** Define objective function\n```python\ndef objective_function(rho):\n    order = [15,8]\n\n    sim = torcwa.rcwa(freq=1/lamb0,order=order,L=L,dtype=sim_dtype,device=device)\n    sim.add_input_layer(eps=substrate_eps)\n    sim.set_incident_angle(inc_ang=inc_ang,azi_ang=azi_ang)\n    layer0_eps = rho*silicon_eps + (1.-rho)\n    sim.add_layer(thickness=layer0_thickness,eps=layer0_eps)\n    sim.solve_global_smatrix()\n    t1xx = sim.S_parameters(orders=[1,0],direction='forward',port='transmission',polarization='xx',ref_order=[0,0])\n    t1yy = sim.S_parameters(orders=[1,0],direction='forward',port='transmission',polarization='yy',ref_order=[0,0])\n    t1xy = sim.S_parameters(orders=[1,0],direction='forward',port='transmission',polarization='xy',ref_order=[0,0])\n    t1yx = sim.S_parameters(orders=[1,0],direction='forward',port='transmission',polarization='yx',ref_order=[0,0])\n\n    T1_sum = torch.abs(t1xx)**2 + torch.abs(t1yy)**2 + torch.abs(t1xy)**2 + torch.abs(t1yx)**2\n    return T1_sum\n```\n\n* Objective function should return single scalar value\n\n\u003cbr/\u003e\n\n**3.** Define hyperparameters and initialize\n```python\ngar_initial = 0.02\nbeta1 = 0.9\nbeta2 = 0.999\nepsilon = 1.e-8\niter_max = 800\nbeta = np.exp(np.arange(start=0,stop=iter_max)*np.log(1000)/iter_max)\ngar = gar_initial * 0.5*(1+np.cos(np.arange(start=0,stop=iter_max)*np.pi/iter_max))\n\n# blur kernel\nblur_radius = 20.\ndx, dy = L[0]/torcwa.rcwa_geo.nx, L[1]/torcwa.rcwa_geo.ny\nx_kernel_axis = (torch.arange(torcwa.rcwa_geo.nx,dtype=geo_dtype,device=device)-(torcwa.rcwa_geo.nx-1)/2)*dx\ny_kernel_axis = (torch.arange(torcwa.rcwa_geo.ny,dtype=geo_dtype,device=device)-(torcwa.rcwa_geo.ny-1)/2)*dy\nx_kernel_grid, y_kernel_grid = torch.meshgrid(x_kernel_axis,y_kernel_axis,indexing='ij')\ng = torch.exp(-(x_kernel_grid**2+y_kernel_grid**2)/blur_radius**2)\ng = g/torch.sum(g)\ng_fft = torch.fft.fftshift(torch.fft.fft2(torch.fft.ifftshift(g)))\n\ntorch.manual_seed(0)\nrho = torch.rand((torcwa.rcwa_geo.nx,torcwa.rcwa_geo.ny),dtype=geo_dtype,device=device)\nrho = (rho + torch.fliplr(rho))/2\nrho_fft = torch.fft.fftshift(torch.fft.fft2(torch.fft.ifftshift(rho)))\nrho = torch.real(torch.fft.fftshift(torch.fft.ifft2(torch.fft.ifftshift(rho_fft*g_fft))))\nmomentum = torch.zeros_like(rho)\nvelocity = torch.zeros_like(rho)\n\nrho_history = []\nFoM_history = []\n```\n\n* PyTorch built-in optimization tool can be utilized instead.\n\n* Define blurring kernel for fabrication feasibility of pattern\n\n* 'rho' is pattern to optimize\n\n* Hyperparameters\n\t* gar_initial: Initial learning rate\n\t* beta1: Momentum coefficients in ADAM optimizer\n\t* beta2: Velocity coefficients in ADAM optimizer\n\t* epsilon: Parameter for preventing division by zero\n\t* iter_max: Maximum number of iteration\n\t* beta: Binarize coefficient of pattern at each iteration\n\t* gar: Learning rate at each iteration\n\n\u003cbr/\u003e\n\n**4.** Perform optimization\n```python\nstart_time = time.time()\nfor it in range(0,iter_max):\n    rho.requires_grad_(True)\n    rho_fft = torch.fft.fftshift(torch.fft.fft2(torch.fft.ifftshift(rho)))\n    rho_bar = torch.real(torch.fft.fftshift(torch.fft.ifft2(torch.fft.ifftshift(rho_fft*g_fft))))\n    rho_tilda = 1/2 + torch.tanh(2*beta[it]*rho_bar-beta[it])/(2*np.math.tanh(beta[it]))\n\n    FoM = objective_function(rho_tilda)\n    FoM.backward()\n\n    with torch.no_grad():\n        rho_gradient = rho.grad\n        rho.grad = None\n\n        rho_history.append(rho_tilda.detach().cpu().numpy())\n        FoM = float(FoM.detach().cpu().numpy())\n        FoM_history.append(FoM)\n\n        momentum = (beta1*momentum + (1-beta1)*rho_gradient)\n        velocity = (beta2*velocity + (1-beta2)*(rho_gradient**2))\n        rho += gar[it]*(momentum / (1-beta1**(it+1))) / torch.sqrt((velocity / (1-beta2**(it+1))) + epsilon)\n        rho[rho\u003e1] = 1\n        rho[rho\u003c0] = 0\n        rho = (rho + torch.fliplr(rho))/2\n\n        end_time = time.time()\n        elapsed_time = end_time - start_time\n        print('Iteration:',it,'/ FoM:',int(FoM*10000)/10000,'/ Elapsed time:',str(int(elapsed_time))+' s')\n```\n\n* Sequence\n\n\t1. Declare 'requires_grad_(True)' for parameters to optimize\n\n\t2. After some manipulation of the parameters, the FoM is derived by substituting it into the objective function.\n\n\t3. Execute 'FoM.backward()' to calculate gradient\n\t\n\t4. Gradient is obtained using 'rho.grad'.\n\n\t5. Update the parameters according to the optimization algorithm.\n\n\u003cbr/\u003e\n\nAcknowledgements\n----------------\nThis work was supported by the National Research Foundation of Korea (NRF) grant funded by the Korea government (MSIT) (No. 2020R1A2B5B02002730) and Samsung Electronics Co., Ltd (IO201214-08164-01).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkch3782%2Ftorcwa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkch3782%2Ftorcwa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkch3782%2Ftorcwa/lists"}