# Start-to-Finish Example: Unit Testing `GiRaFFE_NRPy`: Conservative-to-Primitive and Primitive-to-Conservative Solvers

## Author: Patrick Nelson

**Notebook Status:** Validated

**Validation Notes:** This module will validate the routines in [Tutorial-GiRaFFE_NRPy-C2P_P2C](Tutorial-GiRaFFE_NRPy-C2P_P2C.ipynb).

### NRPy+ Source Code for this module:
* [GiRaFFE_NRPy/GiRaFFE_NRPy_C2P_P2C.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_C2P_P2C.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy-C2P_P2C.ipynb) Generates the conservative-to-primitive and primitive-to-conservative solvers.

## Introduction:

This notebook validates the NRPyfied C2P and P2C solvers against the original `GiRaFFE` code. This will be done at a point with a random but realistic spacetime and a variety of Poynting fluxes and Valencia velocities to test edge cases.

When this notebook is run, the significant digits of agreement between the old `GiRaFFE` and new `GiRaFFE_NRPy` versions of the algorithm outputs of Poynting flux and Valencia three-velocity will be printed to the screen right after the code is run [here](#compile_run).

# Table of Contents
$$\label{toc}$$

This notebook is organized as follows

1. [Step 1](#setup): Set up core functions and parameters for unit testing the C2P and P2C algorithms
    1. [Step 1.a](#c2p) Conservative-to-Primitive Solver
    1. [Step 1.b](#p2c) Primitive-to-Conservative Solver
    1. [Step 1.c](#download) Download original `GiRaFFE` files
    1. [Step 1.d](#free_params) Output C codes needed for declaring and setting Cparameters; also set `free_parameters.h`
    1. [Step 1.e](#interface) Create dummy files for the CCTK version of the code
1. [Step 2](#mainc): `C2P_P2C_unit_test.c`: The Main C Code
    1. [Step 2.a](#compile_run): Compile and run the code to validate the output
1. [Step 3](#drift_notes): Output this notebook to $\LaTeX$-formatted PDF file
1. [Step 4](#latex_pdf_output): Output this notebook to $\LaTeX$-formatted PDF file

# Step 1: Set up core functions and parameters for unit testing the C2P and P2C algorithms \[Back to [top](#toc)\]

$$\label{setup}$$

We'll start by appending the relevant paths to `sys.path` so that we can access sympy modules in other places. Then, we'll import NRPy+ core functionality and set up a directory in which to carry out our test. We will also declare the gridfunctions that are needed for this portion of the code.

```python
import shutil, os, sys           # Standard Python modules for multiplatform OS-level functions
# First, we'll add the parent directory to the list of directories Python will check for modules.
nrpy_dir_path = os.path.join("..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)
nrpy_dir_path = os.path.join("..","..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

from outputC import *            # NRPy+: Core C code output module
import sympy as sp               # SymPy: The Python computer algebra package upon which NRPy+ depends
import finite_difference as fin  # NRPy+: Finite difference C code generation module
import NRPy_param_funcs as par   # NRPy+: Parameter interface
import grid as gri               # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp         # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import cmdline_helper as cmd     # NRPy+: Multi-platform Python command-line interface

out_dir = "Validation/"
cmd.mkdir(out_dir)

thismodule = "Start_to_Finish_UnitTest-GiRaFFE_NRPy-C2P_P2C"

# Register the gridfunctions we need for this function
StildeD = ixp.register_gridfunctions_for_single_rank1("EVOL","StildeD")
BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","BU")
ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","ValenciavU")
gammaDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL","gammaDD","sym01")
alpha = gri.register_gridfunctions("AUXEVOL","alpha")
betaU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","betaU")

```

## Step 1.a: Conservative-to-Primitive Solver \[Back to [top](#toc)\]

$$\label{c2p}$$

First, we'll use NRPy+ to build the C function that will apply fixes to  $\tilde{S}_i$ and recompute the velocity to match, along with the current sheet prescription. Note that the NRPy+ version of this code also speed-limits the velocities.

```python
import GiRaFFE_NRPy.GiRaFFE_NRPy_C2P_P2C as C2P_P2C
C2P_P2C.GiRaFFE_NRPy_C2P(StildeD,BU,gammaDD,betaU,alpha)

values_to_print = [
                   lhrh(lhs=gri.gfaccess("in_gfs","StildeD0"),rhs=C2P_P2C.outStildeD[0]),
                   lhrh(lhs=gri.gfaccess("in_gfs","StildeD1"),rhs=C2P_P2C.outStildeD[1]),
                   lhrh(lhs=gri.gfaccess("in_gfs","StildeD2"),rhs=C2P_P2C.outStildeD[2]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU0"),rhs=C2P_P2C.ValenciavU[0]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU1"),rhs=C2P_P2C.ValenciavU[1]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU2"),rhs=C2P_P2C.ValenciavU[2])
                  ]

desc = "Apply fixes to \tilde{S}_i and recompute the velocity to match with current sheet prescription."
name = "GiRaFFE_NRPy_cons_to_prims"
outCfunction(
    outfile  = os.path.join(out_dir,name+".h"), desc=desc, name=name,
    params   ="const paramstruct *params,REAL *xx[3],REAL *auxevol_gfs,REAL *in_gfs",
    body     = fin.FD_outputC("returnstring",values_to_print,params="outCverbose=False").replace("IDX4","IDX4S"),
    loopopts ="AllPoints,Read_xxs")

```

    Output C function GiRaFFE_NRPy_cons_to_prims() to file Validation/GiRaFFE_NRPy_cons_to_prims.h

## Step 1.b: Primitive-to-Conservative Solver \[Back to [top](#toc)\]

$$\label{p2c}$$

Now, we'll output the function to solve for $\tilde{S}_i$ from the Valencia 3-velocity after the current sheet prescription is applied.

```python
# Declare this symbol:
sqrt4pi = par.Cparameters("REAL",thismodule,"sqrt4pi","sqrt(4.0*M_PI)")
TINYDOUBLE = par.Cparameters("REAL",thismodule,"TINYDOUBLE",1e-100)

C2P_P2C.GiRaFFE_NRPy_P2C(gammaDD,betaU,alpha,  ValenciavU,BU, sqrt4pi)

values_to_print = [
                   lhrh(lhs=gri.gfaccess("in_gfs","StildeD0"),rhs=sp.simplify(C2P_P2C.StildeD[0])),
                   lhrh(lhs=gri.gfaccess("in_gfs","StildeD1"),rhs=sp.simplify(C2P_P2C.StildeD[1])),
                   lhrh(lhs=gri.gfaccess("in_gfs","StildeD2"),rhs=sp.simplify(C2P_P2C.StildeD[2])),
                  ]

desc = "Recompute StildeD after current sheet fix to Valencia 3-velocity to ensure consistency between conservative & primitive variables."
name = "GiRaFFE_NRPy_prims_to_cons"
outCfunction(
    outfile  = os.path.join(out_dir,name+".h"), desc=desc, name=name,
    params   ="const paramstruct *params,REAL *xx[3],REAL *auxevol_gfs,REAL *in_gfs",
    body     = fin.FD_outputC("returnstring",values_to_print,params="outCverbose=False").replace("IDX4","IDX4S"),
    loopopts ="AllPoints")

```

    Output C function GiRaFFE_NRPy_prims_to_cons() to file Validation/GiRaFFE_NRPy_prims_to_cons.h

## Step 1.c: Download original `GiRaFFE` files \[Back to [top](#toc)\]

$$\label{download}$$

Here, we download the relevant portion of the original `GiRaFFE` code from Bitbucket.

```python
# First download the original IllinoisGRMHD source code
import urllib
original_file_url  = ["https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/GiRaFFE/src/GiRaFFE_headers.h",
                      "https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/GiRaFFE/src/inlined_functions.C",
                      "https://bitbucket.org/zach_etienne/wvuthorns/raw/231af720ccf3f1af50f7cce4a86b410fc8ea2e51/GiRaFFE/src/driver_conserv_to_prims_FFE.C",
                      "https://bitbucket.org/zach_etienne/wvuthorns/raw/231af720ccf3f1af50f7cce4a86b410fc8ea2e51/GiRaFFE/src/compute_conservatives_FFE.C",
                      "https://bitbucket.org/zach_etienne/wvuthorns/raw/231af720ccf3f1af50f7cce4a86b410fc8ea2e51/GiRaFFE/src/convert_ADM_to_BSSN__enforce_detgtij_eq_1__and_compute_gtupij.C"
                     ]
original_file_name = ["GiRaFFE_headers.h",
                      "inlined_functions.C",
                      "driver_conserv_to_prims_FFE.C",
                      "compute_conservatives_FFE.C",
                      "convert_ADM_to_BSSN__enforce_detgtij_eq_1__and_compute_gtupij.C"
                     ]

for i in range(len(original_file_url)):
    original_file_path = os.path.join(out_dir,original_file_name[i])

    # Then download the original IllinoisGRMHD source code
    # We try it here in a couple of ways in an attempt to keep
    # the code more portable
    try:
        original_file_code = urllib.request.urlopen(original_file_url[i]).read().decode('utf-8')
    except:
        original_file_code = urllib.urlopen(original_file_url[i]).read().decode('utf-8')

    # Write down the file the original IllinoisGRMHD source code
    with open(original_file_path,"w") as file:
        file.write(original_file_code)

```

## Step 1.d: Output C codes needed for declaring and setting Cparameters; also set `free_parameters.h` \[Back to [top](#toc)\]

$$\label{free_params}$$

Based on declared NRPy+ Cparameters, first we generate `declare_Cparameters_struct.h`, `set_Cparameters_default.h`, and `set_Cparameters[-SIMD].h`.

Then we output `free_parameters.h`, which sets some basic grid parameters as well as the speed limit parameter we need for this function.

```python
# Step 3.d.i: Generate declare_Cparameters_struct.h, set_Cparameters_default.h, and set_Cparameters[-SIMD].h
# par.generate_Cparameters_Ccodes(os.path.join(out_dir))

# Step 3.d.ii: Set free_parameters.h
with open(os.path.join(out_dir,"free_parameters.h"),"w") as file:
    file.write("""
// Set free-parameter values.

const int NGHOSTS = 0;

// Set free-parameter values for the initial data.
// Override parameter defaults with values based on command line arguments and NGHOSTS.
const int Nx0x1x2 = 1;
params.Nxx0 = Nx0x1x2;
params.Nxx1 = Nx0x1x2;
params.Nxx2 = Nx0x1x2;
params.Nxx_plus_2NGHOSTS0 = params.Nxx0 + 2*NGHOSTS;
params.Nxx_plus_2NGHOSTS1 = params.Nxx1 + 2*NGHOSTS;
params.Nxx_plus_2NGHOSTS2 = params.Nxx2 + 2*NGHOSTS;
// Step 0d: Set up space and time coordinates
// Step 0d.i: Declare \Delta x^i=dxx{0,1,2} and invdxx{0,1,2}, as well as xxmin[3] and xxmax[3]:
const REAL xxmin[3] = {0.0,0.0,0.0};
const REAL xxmax[3] = {0.1,0.1,0.1};

params.dxx0 = (xxmax[0] - xxmin[0]) / ((REAL)params.Nxx0);
params.dxx1 = (xxmax[1] - xxmin[1]) / ((REAL)params.Nxx1);
params.dxx2 = (xxmax[2] - xxmin[2]) / ((REAL)params.Nxx2);
params.invdx0 = 1.0 / params.dxx0;
params.invdx1 = 1.0 / params.dxx1;
params.invdx2 = 1.0 / params.dxx2;

params.GAMMA_SPEED_LIMIT = 2000.0;
\n""")

# Generates declare_Cparameters_struct.h, set_Cparameters_default.h, and set_Cparameters[-SIMD].h
par.generate_Cparameters_Ccodes(os.path.join(out_dir))
```

## Step 1.e: Create dummy files for the CCTK version of the code \[Back to [top](#toc)\]

$$\label{interface}$$

The original `GiRaFFE` code depends on some functionalities of the CCTK. Since we only care about this one small function, we can get around this by creating some nearly-empty, non-functional files that can be included to satisfy the pre-processor without changing functionality. We will later replace what little functionality we need with some basic global variables and macros.

```python
#incldue "cctk.h"
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"
#include "Symmetry.h"
with open(os.path.join(out_dir,"cctk.h"),"w") as file:
    file.write("""//""")

with open(os.path.join(out_dir,"cctk_Arguments.h"),"w") as file:
    file.write("""#define DECLARE_CCTK_ARGUMENTS //
#define CCTK_ARGUMENTS void
""")

with open(os.path.join(out_dir,"cctk_Parameters.h"),"w") as file:
    file.write("""#define DECLARE_CCTK_PARAMETERS //
""")

with open(os.path.join(out_dir,"Symmetry.h"),"w") as file:
    file.write("""//""")

```

# Step 2: `C2P_P2C_unit_test.c`: The Main C Code \[Back to [top](#toc)\]

$$\label{mainc}$$

Now that we have our vector potential and analytic magnetic field to compare against, we will start writing our unit test. We'll also import common C functionality, define `REAL`, the number of ghost zones, and the faces, and set the standard macros for NRPy+ style memory access.

```python
%%writefile $out_dir/C2P_P2C_unit_test.C
// These are common packages that we are likely to need.
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "string.h" // Needed for strncmp, etc.
#include "stdint.h" // Needed for Windows GCC 6.x compatibility
#include <time.h>   // Needed to set a random seed.

#define REAL double
#include "declare_Cparameters_struct.h"

// Standard NRPy+ memory access:
#define IDX4S(g,i,j,k) \
( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) + Nxx_plus_2NGHOSTS2 * (g) ) ) )

// Memory access definitions for NRPy+
#define GAMMADD00GF 0
#define GAMMADD01GF 1
#define GAMMADD02GF 2
#define GAMMADD11GF 3
#define GAMMADD12GF 4
#define GAMMADD22GF 5
#define BETAU0GF 6
#define BETAU1GF 7
#define BETAU2GF 8
#define ALPHAGF 9
#define BU0GF 10
#define BU1GF 11
#define BU2GF 12
#define VALENCIAVU0GF 13
#define VALENCIAVU1GF 14
#define VALENCIAVU2GF 15
#define NUM_AUXEVOL_GFS 16

#define STILDED0GF 0
#define STILDED1GF 1
#define STILDED2GF 2
#define NUM_EVOL_GFS 3

// Include the functions that we want to test:
#include "GiRaFFE_NRPy_cons_to_prims.h"
#include "GiRaFFE_NRPy_prims_to_cons.h"

// Define CCTK macros
#define CCTK_REAL double
#define CCTK_INT int
struct cGH{};
const cGH* cctkGH;

// GiRaFFE parameters in ETK
const CCTK_REAL min_radius_inside_of_which_conserv_to_prims_FFE_and_FFE_evolution_is_DISABLED = -1;
const int current_sheet_null_v = 1;

// More definitions to interface with ETK code:
const int cctk_lsh[3] = {1,1,1};
CCTK_REAL gxx[1];
CCTK_REAL gxy[1];
CCTK_REAL gxz[1];
CCTK_REAL gyy[1];
CCTK_REAL gyz[1];
CCTK_REAL gzz[1];
CCTK_REAL alp[1];
CCTK_REAL gtxx[1];
CCTK_REAL gtxy[1];
CCTK_REAL gtxz[1];
CCTK_REAL gtyy[1];
CCTK_REAL gtyz[1];
CCTK_REAL gtzz[1];
CCTK_REAL gtupxx[1];
CCTK_REAL gtupxy[1];
CCTK_REAL gtupxz[1];
CCTK_REAL gtupyy[1];
CCTK_REAL gtupyz[1];
CCTK_REAL gtupzz[1];
CCTK_REAL phi_bssn[1];
CCTK_REAL psi_bssn[1];
CCTK_REAL lapm1[1];
CCTK_REAL betax[1];
CCTK_REAL betay[1];
CCTK_REAL betaz[1];
CCTK_REAL mhd_st_x[1];
CCTK_REAL mhd_st_y[1];
CCTK_REAL mhd_st_z[1];
CCTK_REAL vx[1];
CCTK_REAL vy[1];
CCTK_REAL vz[1];
CCTK_REAL Bx[1];
CCTK_REAL By[1];
CCTK_REAL Bz[1];
CCTK_REAL x[1];
CCTK_REAL y[1];
CCTK_REAL z[1];
CCTK_REAL r[1];

// Define dz in CCTK
CCTK_REAL cactus_dz;
#define CCTK_DELTA_SPACE(i) cactus_dz

CCTK_REAL GAMMA_SPEED_LIMIT = 2000.0;

// Dummy ETK function:
#define CCTK_GFINDEX3D(cctkGH,i,j,k) 0
#define CCTK_VInfo(a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12) //
#define CCTK_VWarn(b01,b02,b03,b04,b05,b06,b07,b08,b09,b10,b11,b12,b13,b14,b15,b16,b17,b18,b19,b20,b21,b22,b23,b24,b25) //

#include "driver_conserv_to_prims_FFE.C"
#include "compute_conservatives_FFE.C"
#include "convert_ADM_to_BSSN__enforce_detgtij_eq_1__and_compute_gtupij.C"

int main() {
    paramstruct params;
#include "set_Cparameters_default.h"

    // Step 0c: Set free parameters, overwriting Cparameters defaults
    //          by hand or with command-line input, as desired.
#include "free_parameters.h"
#include "set_Cparameters-nopointer.h"

    // Now that we've calculated dxx2,  we can define a cactus equivalent
    cactus_dz = dxx2;
    // We'll define all indices to be 0. No need to complicate memory access
    const int i0 = 0;
    const int i1 = 0;
    const int i2 = 0;

    // This is the array to which we'll write the NRPy+ variables.
    REAL *auxevol_gfs  = (REAL *)malloc(sizeof(REAL) * NUM_AUXEVOL_GFS);
    REAL *evol_gfs  = (REAL *)malloc(sizeof(REAL) * NUM_EVOL_GFS);

    // Step 0e: Set up cell-centered Cartesian coordinate grids
    REAL *xx[3];
    xx[0] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS0);
    xx[1] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS1);
    xx[2] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS2);
    for(int j=0;j<Nxx_plus_2NGHOSTS0;j++) xx[0][j] = xxmin[0] + (j-NGHOSTS)*dxx0;
    for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) xx[1][j] = xxmin[1] + (j-NGHOSTS)*dxx1;
    for(int j=0;j<Nxx_plus_2NGHOSTS2;j++) xx[2][j] = xxmin[2] + (j-NGHOSTS)*dxx2;
    x[0] = xx[0][0];
    y[0] = xx[1][0];
    z[0] = xx[2][0];
    r[0] = sqrt(xx[0][0]*xx[0][0] + xx[1][0]*xx[1][0] + xx[2][0]*xx[2][0]);

    // Now, it's time to make the random numbers.
    //const long int seed = time(NULL); // seed = 1570632212; is an example of a seed that produces
                                      // bad agreement for high speeds
    //srand(seed); // Set the seed
    //printf("seed for random number generator = %ld; RECORD IF AGREEMENT IS BAD\\n\\n",seed);
    // We take care to make sure the corresponding quantities have the SAME value.
    auxevol_gfs[IDX4S(ALPHAGF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*0.2-0.1;
    const double alphaL = auxevol_gfs[IDX4S(ALPHAGF, i0,i1,i2)];
    alp[0] = alphaL;

    auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*0.2-0.1;

    // Generated by NRPy+:
       const double gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)];
       const double gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)];
       const double gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)];
       const double gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)];
       const double gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)];
       const double gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)];
       /*
        * NRPy+ Finite Difference Code Generation, Step 2 of 1: Evaluate SymPy expressions and write to main memory:
        */
       const double tmp0 = gammaDD11*gammaDD22;
       const double tmp1 = pow(gammaDD12, 2);
       const double tmp2 = gammaDD02*gammaDD12;
       const double tmp3 = pow(gammaDD01, 2);
       const double tmp4 = pow(gammaDD02, 2);
       const double tmp5 = gammaDD00*tmp0 - gammaDD00*tmp1 + 2*gammaDD01*tmp2 - gammaDD11*tmp4 - gammaDD22*tmp3;
       const double tmp6 = 1.0/tmp5;

    // Set the ETK metric:
    gxx[0] = gammaDD00;
    gxy[0] = gammaDD01;
    gxz[0] = gammaDD02;
    gyy[0] = gammaDD11;
    gyz[0] = gammaDD12;
    gzz[0] = gammaDD22;

    auxevol_gfs[IDX4S(BETAU0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.2-0.1;
    const double betaxL = auxevol_gfs[IDX4S(BETAU0GF, i0,i1,i2)];
    betax[0] = betaxL;
    auxevol_gfs[IDX4S(BETAU1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.2-0.1;
    const double betayL = auxevol_gfs[IDX4S(BETAU1GF, i0,i1,i2)];
    betay[0] = betayL;
    auxevol_gfs[IDX4S(BETAU2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.02-0.01;
    const double betazL = auxevol_gfs[IDX4S(BETAU2GF, i0,i1,i2)];
    betaz[0] = betazL;

    /* Generate physically meaningful speeds */
    auxevol_gfs[IDX4S(VALENCIAVU0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    vx[0] = alphaL*auxevol_gfs[IDX4S(VALENCIAVU0GF, i0,i1,i2)]-betaxL;
    auxevol_gfs[IDX4S(VALENCIAVU1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    vy[0] = alphaL*auxevol_gfs[IDX4S(VALENCIAVU1GF, i0,i1,i2)]-betayL;
    auxevol_gfs[IDX4S(VALENCIAVU2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    vz[0] = alphaL*auxevol_gfs[IDX4S(VALENCIAVU2GF, i0,i1,i2)]-betazL;
    /* Superluminal speeds for testing */
    /*auxevol_gfs[IDX4S(VALENCIAVU0GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    vx[0] = alphaL*auxevol_gfs[IDX4S(VALENCIAVU0GF, i0,i1,i2)]-betaxL;
    auxevol_gfs[IDX4S(VALENCIAVU1GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    vy[0] = alphaL*auxevol_gfs[IDX4S(VALENCIAVU1GF, i0,i1,i2)]-betayL;
    auxevol_gfs[IDX4S(VALENCIAVU2GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    vz[0] = alphaL*auxevol_gfs[IDX4S(VALENCIAVU2GF, i0,i1,i2)]-betazL;*/

    auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Bx[0] = auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)];
    auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    By[0] = auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)];
    auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Bz[0] = auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)];

    evol_gfs[IDX4S(STILDED0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    mhd_st_x[0] = evol_gfs[IDX4S(STILDED0GF, i0,i1,i2)];
    evol_gfs[IDX4S(STILDED1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    mhd_st_y[0] = evol_gfs[IDX4S(STILDED1GF, i0,i1,i2)];
    evol_gfs[IDX4S(STILDED2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    mhd_st_z[0] = evol_gfs[IDX4S(STILDED2GF, i0,i1,i2)];

    GiRaFFE_conserv_to_prims_FFE();
    GiRaFFE_NRPy_cons_to_prims(&params,xx,auxevol_gfs,evol_gfs);
    GiRaFFE_NRPy_prims_to_cons(&params,xx,auxevol_gfs,evol_gfs);

    //printf("Checking the Poynting Fluxes:\n");
    printf("%.1f, %.1f, %.1f\n",1.0-log10(2.0*fabs(evol_gfs[IDX4S(STILDED0GF, i0,i1,i2)]-mhd_st_x[0])/(fabs(evol_gfs[IDX4S(STILDED0GF, i0,i1,i2)])+fabs(mhd_st_x[0]))),
                                     1.0-log10(2.0*fabs(evol_gfs[IDX4S(STILDED1GF, i0,i1,i2)]-mhd_st_y[0])/(fabs(evol_gfs[IDX4S(STILDED1GF, i0,i1,i2)])+fabs(mhd_st_y[0]))),
                                     1.0-log10(2.0*fabs(evol_gfs[IDX4S(STILDED2GF, i0,i1,i2)]-mhd_st_z[0])/(fabs(evol_gfs[IDX4S(STILDED2GF, i0,i1,i2)])+fabs(mhd_st_z[0]))));
    //printf("NRPy: %.15e,%.15e,%.15e\n",evol_gfs[IDX4S(STILDED0GF, i0,i1,i2)],evol_gfs[IDX4S(STILDED1GF, i0,i1,i2)],evol_gfs[IDX4S(STILDED2GF, i0,i1,i2)]);
    //printf("CCTK: %.15e,%.15e,%.15e\n",mhd_st_x[0],mhd_st_y[0],mhd_st_z[0]);
    //printf("\n\n");
    //printf("Checking the Valencia Velocities:\n");
    printf("%.1f, %.1f, %.1f\n",1.0-log10(2.0*fabs(auxevol_gfs[IDX4S(VALENCIAVU0GF, i0,i1,i2)]-(vx[0]+betaxL)/alphaL)/(fabs(auxevol_gfs[IDX4S(VALENCIAVU0GF, i0,i1,i2)])+fabs((vx[0]+betaxL)/alphaL))),
                                     1.0-log10(2.0*fabs(auxevol_gfs[IDX4S(VALENCIAVU1GF, i0,i1,i2)]-(vy[0]+betayL)/alphaL)/(fabs(auxevol_gfs[IDX4S(VALENCIAVU1GF, i0,i1,i2)])+fabs((vy[0]+betayL)/alphaL))),
                                     1.0-log10(2.0*fabs(auxevol_gfs[IDX4S(VALENCIAVU2GF, i0,i1,i2)]-(vz[0]+betazL)/alphaL)/(fabs(auxevol_gfs[IDX4S(VALENCIAVU2GF, i0,i1,i2)])+fabs((vz[0]+betazL)/alphaL))));
    //printf("NRPy: %.15e,%.15e,%.15e\n",auxevol_gfs[IDX4S(VALENCIAVU0GF, i0,i1,i2)],auxevol_gfs[IDX4S(VALENCIAVU1GF, i0,i1,i2)],auxevol_gfs[IDX4S(VALENCIAVU2GF, i0,i1,i2)]);
    //printf("CCTK: %.15e,%.15e,%.15e\n",(vx[0]+betaxL)/alphaL,(vy[0]+betayL)/alphaL,(vz[0]+betazL)/alphaL);
}
```

    Writing Validation//C2P_P2C_unit_test.C

## Step 2.a: Compile and run the code to validate the output \[Back to [top](#toc)\]

$$\label{compile_run}$$

Finally, we can compile and run the code we have written. Once run, this code will output the level of agreement between the two codes and some information to help interpret those numbers.

```python
import time
results_file = "out.txt"

print("Now compiling, should take ~2 seconds...\n")
start = time.time()
# Can't use C here; must use C++
# This command should work on all systems:
!g++ -Ofast -fopenmp -march=native -funroll-loops Validation/C2P_P2C_unit_test.C -o Validation/C2P_P2C_unit_test -lm
end = time.time()
print("Finished in "+str(end-start)+" seconds.\n\n")

os.chdir(os.path.join("Validation"))
cmd.Execute(os.path.join("C2P_P2C_unit_test"),file_to_redirect_stdout=os.path.join(results_file))
os.chdir(os.path.join(".."))
```

    Now compiling, should take ~2 seconds...

    Finished in 0.48241138458251953 seconds.

    (EXEC): Executing `taskset -c 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 ./C2P_P2C_unit_test `...
    (BENCH): Finished executing in 0.20868754386901855 seconds.

Below are the numbers we care about. These are the Significant Digits of Agreement between the corrected StildeD and ValenciavU as computed by NRPy+ and ETK. Each row represents a flux direction; each entry therein corresponds to a component of StildeD. Each pair of outputs should show at least 10 significant digits of agreement.

```python
import numpy as np
with open(os.path.join(out_dir,results_file),"r") as file:
    for i in range(2):
        output = file.readline()
        substrings = output.split(", ")
        sda = [float(i) for i in substrings]
        enough_digits = np.array(sda)>14
        if not enough_digits.all():
            sys.exit(1)
```

## Step 3: Some notes on the drift velocity \[Back to [top](#toc)\]
$$\label{drift_notes}$$

While we have chosen to use the Valencia three-velocity in this version of `GiRaFFE`, we have also frequently made use of a relationship expressing this in terms of the drift velocity, which was used in the original `GiRaFFE`. The usefulness of this relationship to drift velocity extends beyond merely translating the original code. As discussed in [Paschalidis, et al.](https://arxiv.org/pdf/1310.3274.pdf), Sec. III.A (just above Eq. 45, with a proof in Appendix A), there is a one-parameter family of velocity definitions that fulfill the GRFFE conditions. The drift velocity sets this parameter to 0, which minimizes the Lorentz factor and *guarantees* that the four-velocity and magnetic fields are orthogonal to each other. This simplifies the form of $b^\mu$ and quantities that depend on it.

This must be taken into account in developing this unit test, because NRPy+'s GRFFE module defaults to using a definition of $b^\mu$ that does not assume that this criterion is met, while the original `GiRaFFE` code assumes this in its C2P and P2C solvers. So, if we do not guarantee that our test data fulfills this criterion, these two different routines will produce different results. We will now go through the derivation of the equation used by `GiRaFFE` from first principles to show where this extra term appears.

This is the equation used by `GiRaFFE`, pulled from Eqs. 47 and 85 of [this paper](https://arxiv.org/abs/1310.3274):
$$\tilde{S}_i = \gamma_{ij} \frac{\bar{v}^j \sqrt{\gamma}B^2}{4 \pi},$$
or $$\tilde{S}_i = \gamma_{ij} \frac{(v^j + \beta^j) \sqrt{\gamma}B^2}{4 \pi \alpha},$$
where $\bar{v}^j$ is the Valencia 3-velocity and $v^j$ is the drift velocity.

In IllinoisGRMHD (IGM), the expression used is $\tilde{S}_i = \alpha \sqrt{\gamma} T^0_{{\rm EM}i},$
where
\begin{align}
T^{\mu\nu}_{\rm EM} &= b^2 u^{\mu} u^{\nu} + \frac{1}{2} b^2 g^{\mu\nu} - b^\mu b^\nu \\
b^0 &= \frac{u_j B^j}{\sqrt{4\pi} \alpha} \\
b^i &= \frac{B^i + (u_j B^j) u^i}{\sqrt{4\pi} \alpha u^0} \\
u^i &= u^0 v^i.
\end{align}

Now, we'll work to bridge the gap between the expression in terms of the stress-energy tensor and that used in `GiRaFFE`. Consider that $T^0_{{\rm EM}i} = g_{i \mu} T^{0 \mu}_{{\rm EM}}.$ We'll thus start with the index lowering operation.
\begin{align}
g_{\mu \xi} T^{\xi \nu}_{\rm EM} &= g_{\mu \xi} \left( b^2 u^{\xi} u^{\nu} + \frac{1}{2} b^2 g^{\xi\nu} - b^\xi b^\nu \right) \\
&= b^2 u_{\mu} u^{\nu} + \frac{1}{2} b^2 \delta^\nu_\mu - b_\mu b^\nu.
\end{align}

After doing so, it is still apparent that we will need $b^2 = g_{\mu\nu} b^\mu b^\nu$, where
$$
g_{\mu\nu} = \begin{pmatrix}
-\alpha^2 + \beta^k \beta_k & \beta_i \\
\beta_j & \gamma_{ij}
\end{pmatrix}.
$$
Expanding out the implied sum, we see that
\begin{align}
b^2 &= g_{00} b^0 b^0 + g_{i0} b^i b^0 + g_{0j} b^0 b^j + g_{ij} b^i b^j \\
&= \left(-\alpha^2 + \beta^k \beta_k\right) b^0 b^0 + \beta_i b^i b^0 + \beta_j b^0 b^j + \gamma_{ij} b^i b^j \\
&= \left(-\alpha^2 + \beta^k \beta_k\right) b^0 b^0 + 2 \beta_i b^i b^0 + \gamma_{ij} b^i b^j \\
\end{align}

Now, it will be useful to plug in the definition of $b^\mu$. Recall that
\begin{align}
b^0 &= \frac{u_j B^j}{\sqrt{4\pi} \alpha} \\
b^i &= \frac{B^i + (u_j B^j) u^i}{\sqrt{4\pi} \alpha u^0} \\
\end{align}

Then
\begin{align}
b^2 &= \frac{1}{4 \pi \alpha^2} \left( -\alpha^2 (u_k B^k)^2 + \beta^k \beta_k (u_k B^k)^2 + 2 \beta_i (u_k B^k) (B^i + (u_k B^k) u^i)/u^0 + \gamma_{ij}(B^i + (u_k B^k) u^i)(B^j + (u_k B^k) u^j)/(u^0)^2 \right) \\
\end{align}

This ultimately reduces to (from Paschalidis, et al.):
\begin{align}
b^2 &= \frac{B^2 + (u_\mu B^\mu)^2}{4 \pi \alpha^2 (u^0)^2} \\
\end{align}

So, we insert this into the stress energy tensor:
$$
T^0_{{\rm EM}i} = \frac{B^2 + (u_\mu B^\mu)^2}{4 \pi \alpha^2 (u^0)^2} u_i u^0 - \gamma_{ij} b^j b^0
$$
Note that since $B^\mu$ is purely spatial, $B^0=0$, thus $u_\mu B^\mu = u_k B^k$

\begin{align}
T^0_{{\rm EM}i} &= \frac{B^2 + (u_k B^k)^2}{4 \pi \alpha^2 u^0} u_i - \gamma_{ij} \frac{(B^j + (u_k B^k) u^j)(u_k B^k)}{4 \pi \alpha^2 u^0} \\
&= \frac{1}{4 \pi \alpha^2 u^0} \left( B^2 u_i + (u_k B^k)^2 u_i - \gamma_{ij}(B^j + (u_k B^k) u^j)(u_k B^k)\right) \\
&= \frac{1}{4 \pi \alpha^2 u^0} \left( B^2 u_i + (u_k B^k)^2 u_i - (B_i + (u_k B^k) u_i)(u_k B^k)\right) \\
\end{align}

Now, we will substitute in $u_j/u_0 = \beta_j + \gamma_{lj} v^l$:
\begin{align}
&= \frac{1}{4 \pi \alpha^2} \left( B^2 (\beta_i + \gamma_{li} v^l) - B_i (u_k B^k)\right) \\
&= \frac{1}{4 \pi \alpha^2} \left( B^2 (\gamma_{ij} \beta^j + \gamma_{ij} v^j) - B_i (u_k B^k)\right) \\
\end{align}

Now, we multiply by $\alpha \sqrt{\gamma}$:
\begin{align}
\tilde{S}_i = \alpha \sqrt{\gamma} T^0_{{\rm EM}i} &= \gamma_{ij} \frac{(v^j + \beta^j) \sqrt{\gamma}B^2}{4 \pi \alpha} - \frac{\sqrt{\gamma}B_i (u_k B^k)}{4 \pi \alpha}
\end{align}

We note that there is an extra term here beyond the one that `GiRaFFE` uses for its P2C solver. This term exists because we did not make the assumption that $(u_k B^k) = 0$, which follows from the definition of drift velocity ([see here](https://arxiv.org/pdf/1310.3274.pdf), Sec. III.A, just above Eq. 45). Making that assumption, we get the expression used in `GiRaFFE`:
\begin{align}
\tilde{S}_i = \alpha \sqrt{\gamma} T^0_{{\rm EM}i} &= \gamma_{ij} \frac{(v^j + \beta^j) \sqrt{\gamma}B^2}{4 \pi \alpha}
\end{align}

Normally, this relation does not come into play because $u_i \propto v_i \propto \tilde{S}_i$ and $\tilde{S}_i$ is calculated initially as the cross product of the electric and magnetic fields, so is guaranteed to be orthogonal to $B^i$. Here, however, we did nothing to guarantee this beforehand, so the difference shows up. Note also that the entire derivation becomes far easier if you assume this sooner since $b^0 = 0$ and $b^i = B^i / \left(\sqrt{4 \pi} \alpha u^0\right)$

To handle this more smoothly moving forward, we have added an extra function to the GRFFE module that computes $b^\mu$ under the assumption that $u_j B^j = 0$ for use only in GRFFE codes (i.e., without GRHD). This is desirable for more than just this small case since the extra term might cause small errors to propagate.

# Step 4: Output this notebook to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\]
$$\label{latex_pdf_output}$$

The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename
[Tutorial-Start_to_Finish_UnitTest-GiRaFFE_NRPy-C2P_P2C.pdf](Tutorial-Start_to_Finish_UnitTest-GiRaFFE_NRPy-C2P_P2C.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.)

```python
import cmdline_helper as cmd    # NRPy+: Multi-platform Python command-line interface
# cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-Start_to_Finish_UnitTest-GiRaFFE_NRPy-C2P_P2C",location_of_template_file=os.path.join(".."))
```


```python
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
%matplotlib inline

# import xgboost as xgb
from sklearn.metrics import mean_squared_error

# from xgboost import XGBRegressor
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import StratifiedKFold
from sklearn.ensemble import GradientBoostingRegressor, GradientBoostingClassifier

import seaborn as sns
from pycebox.ice import ice, ice_plot
from matplotlib.cm import seismic

# import sympy as sym
from sympy import *
```

```python
x1,x2,x3,y = [],[],[],[]
N = 1000

for i in range(0,N):

    a = 2
    b = 5

    X1 = np.random.normal(0, 1, size = 1)
    X2 = np.random.uniform(-1, 1, size = 1)
    eps = np.random.normal(-1, 1, size = 1)

    Y = X1*np.random.beta(a, b, size = 1) - 0.3*X2

    x1.append(X1)
    x2.append(X2)

    y.append(Y)

```

```python
y

```

    [array([0.13676667]),
     array([0.42439786]),
     array([-0.14575775]),
     array([-0.45042164]),
     array([0.90227611]),
     array([0.07587778]),
     array([0.34067554]),
     array([-0.57427183]),
     array([0.13622761]),
     array([-0.21181664]),
     array([-0.154186]),
     array([-0.11378588]),
     array([0.00779531]),
     array([0.69060342]),
     array([-0.06110032]),
     array([0.0719829]),
     array([0.87963103]),
     array([0.17856529]),
     array([0.76079087]),
     array([0.94821235]),
     array([0.36777933]),
     array([-0.10484944]),
     array([0.22176246]),
     array([-0.12673842]),
     array([-1.12819451]),
     array([-0.36775866]),
     array([-0.34392671]),
     array([0.22497949]),
     array([-0.93347674]),
     array([-0.38750797]),
     array([0.18170713]),
     array([-0.12065238]),
     array([-0.01184872]),
     array([-0.01318619]),
     array([-0.30321616]),
     array([-0.0900332]),
     array([0.27352024]),
     array([-0.04280928]),
     array([-0.31798229]),
     array([-0.00481156]),
     array([0.30520693]),
     array([-0.21666966]),
     array([-0.00783224]),
     array([-0.60530847]),
     array([0.53083714]),
     array([-0.50077974]),
     array([-0.06240529]),
     array([0.7351583]),
     array([-0.1874292]),
     array([0.18332478]),
     array([0.16482387]),
     array([-0.35667255]),
     array([-0.18942612]),
     array([0.61517556]),
     array([0.10117172]),
     array([0.00749618]),
     array([0.21031176]),
     array([-0.36591778]),
     array([0.39467913]),
     array([0.14858235]),
     array([0.48236579]),
     array([-0.56783279]),
     array([-0.02128555]),
     array([0.28915434]),
     array([0.44616528]),
     array([0.43426751]),
     array([0.26038752]),
     array([-0.40339138]),
     array([-0.63278194]),
     array([0.08503612]),
     array([0.05362452]),
     array([0.17392389]),
     array([0.10106912]),
     array([0.04380756]),
     array([-0.206466]),
     array([-0.49494335]),
     array([0.34598228]),
     array([-0.1802908]),
     array([0.273422]),
     array([-0.79743974]),
     array([-0.43452396]),
     array([0.12887306]),
     array([-0.30696258]),
     array([0.21630489]),
     array([0.22237933]),
     array([0.40488738]),
     array([0.27510008]),
     array([-0.26783153]),
     array([-0.22511465]),
     array([-0.11795171]),
     array([-0.44838743]),
     array([-0.12831046]),
     array([0.9352798]),
     array([-0.32535567]),
     array([-0.04926153]),
     array([0.40008419]),
     array([0.18634376]),
     array([0.11660221]),
     array([0.30473725]),
     array([0.06884248]),
     array([0.55513091]),
     array([0.221419]),
     array([0.04141643]),
     array([0.47191975]),
     array([-0.2483874]),
     array([0.01072099]),
     array([0.1900113]),
     array([0.06388506]),
     array([0.11164528]),
     array([-0.0620428]),
     array([0.88166936]),
     array([0.46417265]),
     array([-1.25048158]),
     array([0.06787552]),
     array([0.24202989]),
     array([0.17395258]),
     array([-0.49108715]),
     array([0.01028883]),
     array([0.51991029]),
     array([-0.51760865]),
     array([0.03193614]),
     array([0.62011326]),
     array([0.22098218]),
     array([0.03120781]),
     array([0.30689125]),
     array([-0.10642725]),
     array([-0.02191101]),
     array([-0.5136422]),
     array([0.6963183]),
     array([0.45183206]),
     array([-0.11477703]),
     array([-0.17590942]),
     array([0.15232721]),
     array([-0.25170454]),
     array([-0.13317683]),
     array([-0.04630061]),
     array([0.15495087]),
     array([0.09069147]),
     array([0.07844863]),
     array([0.00186972]),
     array([0.23524525]),
     array([-0.12601865]),
     array([-1.02985429]),
     array([-0.20677296]),
     array([-0.30768806]),
     array([-0.71006505]),
     array([0.06813358]),
     array([-0.0454938]),
     array([0.0276693]),
     array([-0.52230632]),
     array([0.44168387]),
     array([0.49192503]),
     array([0.22607622]),
     array([-0.01496147]),
     array([-0.26531403]),
     array([0.94583938]),
     array([-0.13682493]),
     array([-0.26962933]),
     array([-0.00175316]),
     array([-0.61421859]),
     array([-0.09682745]),
     array([-0.1780365]),
     array([-0.2102324]),
     array([1.11372259]),
     array([-0.28319513]),
     array([-0.25089189]),
     array([-0.42770714]),
     array([0.21636594]),
     array([0.09119955]),
     array([-0.59714536]),
     array([0.28772214]),
     array([-0.89372502]),
     array([0.06121337]),
     array([0.08390854]),
     array([-0.05573091]),
     array([0.40837454]),
     array([-0.06176063]),
     array([-0.08451679]),
     array([-0.48569786]),
     array([-0.20762166]),
     array([-0.24398044]),
     array([0.04436226]),
     array([0.39102015]),
     array([-0.02429831]),
     array([0.16660278]),
     array([-0.24967498]),
     array([0.47386873]),
     array([0.2120742]),
     array([0.82065117]),
     array([-0.35269748]),
     array([0.37530433]),
     array([0.11942704]),
     array([-0.5140082]),
     array([-0.1463233]),
     array([0.06552404]),
     array([0.45457642]),
     array([0.06269388]),
     array([-0.44854708]),
     array([-0.25366289]),
     array([0.48286612]),
     array([0.08533069]),
     array([0.51013263]),
     array([-0.17978724]),
     array([0.31289154]),
     array([-0.22765933]),
     array([-0.1413953]),
     array([-0.23947089]),
     array([-0.26029344]),
     array([-0.05588849]),
     array([-0.66306435]),
     array([-0.09028764]),
     array([-0.08238333]),
     array([0.13746821]),
     array([0.05961976]),
     array([0.65597132]),
     array([0.28265472]),
     array([-0.06195159]),
     array([-1.12598419]),
     array([-1.05713852]),
     array([0.45315105]),
     array([-0.39995743]),
     array([0.14148654]),
     array([0.06857137]),
     array([-0.70717748]),
     array([-0.02212485]),
     array([-0.11810007]),
     array([0.06602016]),
     array([0.05873746]),
     array([-0.23006512]),
     array([0.11273258]),
     array([0.49536065]),
     array([-0.01252707]),
     array([-0.06733265]),
     array([-0.07854437]),
     array([0.39045775]),
     array([-0.14549299]),
     array([0.41217922]),
     array([0.35203205]),
     array([-0.35227815]),
     array([0.36337596]),
     array([0.18097715]),
     array([0.11198076]),
     array([-0.20170653]),
     array([-0.03697749]),
     array([0.31114894]),
     array([-0.30748673]),
     array([0.03728348]),
     array([-0.42775119]),
     array([-0.03078058]),
     array([-0.51898772]),
     array([-0.81606635]),
     array([-0.1001397]),
     array([0.23328058]),
     array([0.5681076]),
     array([0.88197297]),
     array([-0.26294787]),
     array([-0.12266418]),
     array([-0.85772914]),
     array([-0.58758216]),
     array([-0.38929595]),
     array([0.10196126]),
     array([-0.17632005]),
     array([-0.23435811]),
     array([0.17512113]),
     array([-0.1480833]),
     array([0.36868601]),
     array([-0.27710912]),
     array([-0.28589328]),
     array([0.25028375]),
     array([0.007368]),
     array([0.16036048]),
     array([0.61855967]),
     array([-0.06237322]),
     array([-0.06588406]),
     array([0.07265415]),
     array([0.38308816]),
     array([0.13114343]),
     array([-0.36518227]),
     array([-0.57600694]),
     array([-0.13677205]),
     array([0.41447069]),
     array([-0.04223927]),
     array([-0.00570501]),
     array([-0.60378779]),
     array([0.0901775]),
     array([0.22348855]),
     array([-0.17871372]),
     array([0.09153196]),
     array([0.67070299]),
     array([-0.12938562]),
     array([0.73497262]),
     array([0.10491688]),
     array([-0.3274617]),
     array([0.07237513]),
     array([-0.43614989]),
     array([0.19551859]),
     array([0.15785834]),
     array([-0.18503528]),
     array([-0.40363955]),
     array([0.25201703]),
     array([-0.40314318]),
     array([-0.02243097]),
     array([-0.28090727]),
     array([-0.79052724]),
     array([-0.13982114]),
     array([0.24549033]),
     array([0.53966492]),
     array([0.12472887]),
     array([-0.06090436]),
     array([-0.83204561]),
     array([0.13292919]),
     array([-0.09006918]),
     array([0.63491174]),
     array([0.55657608]),
     array([0.05285171]),
     array([0.02642466]),
     array([-0.26069004]),
     array([0.10982566]),
     array([0.23511564]),
     array([0.26400676]),
     array([0.10250601]),
     array([-0.31161448]),
     array([-0.04259237]),
     array([0.12487849]),
     array([-0.47234679]),
     array([0.12454559]),
     array([-0.0157836]),
     array([-0.04716576]),
     array([-0.03544974]),
     array([-0.14687846]),
     array([-0.09605977]),
     array([-0.32191273]),
     array([0.29056184]),
     array([-0.29853132]),
     array([-0.62573857]),
     array([0.44612523]),
     array([-0.00936218]),
     array([-0.08458959]),
     array([-0.43282346]),
     array([-0.70577897]),
     array([-0.84491423]),
     array([0.12742633]),
     array([-0.05043007]),
     array([-0.18525854]),
     array([0.09528769]),
     array([0.03388609]),
     array([-0.09353193]),
     array([0.50325499]),
     array([0.2514263]),
     array([-0.31483406]),
     array([0.08202627]),
     array([-0.14846857]),
     array([-0.10178635]),
     array([-0.22991842]),
     array([-0.51639275]),
     array([-0.30772486]),
     array([-0.10012439]),
     array([0.25150976]),
     array([-0.21664451]),
     array([0.33299398]),
     array([0.3981783]),
     array([0.67890409]),
     array([0.22753621]),
     array([-0.19297077]),
     array([0.46601]),
     array([-0.01804028]),
     array([0.07237633]),
     array([-0.31623572]),
     array([0.13974004]),
     array([-0.18644952]),
     array([1.18864454]),
     array([-0.41316412]),
     array([-0.26899818]),
     array([0.25905769]),
     array([-0.32469945]),
     array([-0.20163669]),
     array([-0.086446]),
     array([0.42616987]),
     array([0.36922639]),
     array([0.6650453]),
     array([0.23930626]),
     array([0.37396277]),
     array([0.25170132]),
     array([-0.23100503]),
     array([-0.2744647]),
     array([0.06270949]),
     array([-0.00964484]),
     array([-0.67681239]),
     array([1.09680146]),
     array([0.12691978]),
     array([-0.27050362]),
     array([-0.11759931]),
     array([0.17161155]),
     array([-0.0043967]),
     array([0.37007831]),
     array([-0.33570834]),
     array([-0.17488673]),
     array([-0.15179398]),
     array([0.49839929]),
     array([0.17903933]),
     array([0.83069765]),
     array([-0.30208054]),
     array([0.13135659]),
     array([0.24647602]),
     array([1.08401005]),
     array([-0.58900576]),
     array([0.17648526]),
     array([-0.03893796]),
     array([0.03628803]),
     array([-0.28074183]),
     array([-0.21103406]),
     array([0.35378959]),
     array([0.09720313]),
     array([0.09661841]),
     array([-0.03539621]),
     array([-0.1126001]),
     array([0.14565756]),
     array([-0.12701385]),
     array([0.39332791]),
     array([-0.29723769]),
     array([-6.48168192e-05]),
     array([0.17974797]),
     array([0.20651339]),
     array([-0.117083]),
     array([-0.11243065]),
     array([-0.27813166]),
     array([-0.02553731]),
     array([-0.30687695]),
     array([-0.00869682]),
     array([-0.08951239]),
     array([0.2748595]),
     array([0.28312675]),
     array([-0.59556709]),
     array([0.27499143]),
     array([0.26784907]),
     array([-1.01567071]),
     array([-0.01764481]),
     array([0.0322104]),
     array([0.35664879]),
     array([-0.37169692]),
     array([-0.70142584]),
     array([-0.12369906]),
     array([0.2447313]),
     array([-0.43412793]),
     array([-0.2293769]),
     array([-0.17812722]),
     array([-0.26937359]),
     array([-0.27141593]),
     array([-0.40923626]),
     array([0.15928425]),
     array([-0.18508071]),
     array([0.88487874]),
     array([0.47103904]),
     array([-0.35806279]),
     array([-0.03045967]),
     array([-0.12678288]),
     array([0.37707831]),
     array([-0.23767845]),
     array([0.51968106]),
     array([0.241346]),
     array([0.51110177]),
     array([0.33275552]),
     array([0.43800578]),
     array([-0.02848852]),
     array([-0.27816134]),
     array([-0.30277705]),
     array([-0.17103497]),
     array([0.3151219]),
     array([0.25690869]),
     array([0.45548298]),
     array([-0.25815098]),
     array([0.35375736]),
     array([-0.20276872]),
     array([0.02694364]),
     array([0.69843713]),
     array([0.02999642]),
     array([0.08278741]),
     array([0.04255918]),
     array([-0.00131029]),
     array([-0.57027427]),
     array([0.10005608]),
     array([-0.4417364]),
     array([0.29282234]),
     array([-0.25920152]),
     array([-0.3807173]),
     array([-0.09816595]),
     array([0.03894283]),
     array([-0.04395177]),
     array([0.48963916]),
     array([-0.080819]),
     array([0.5459901]),
     array([-0.62250975]),
     array([-0.32055905]),
     array([0.26846323]),
     array([-0.52668322]),
     array([1.25043184]),
     array([-0.30294526]),
     array([0.0493056]),
     array([0.31906966]),
     array([0.87817516]),
     array([-0.3124554]),
     array([0.3670206]),
     array([0.25887909]),
     array([-0.1553605]),
     array([-0.28693441]),
     array([-0.31184996]),
     array([0.27736868]),
     array([-0.04838428]),
     array([0.14861068]),
     array([0.18428792]),
     array([-0.09946484]),
     array([0.29835724]),
     array([0.0095755]),
     array([-0.24739655]),
     array([0.10544755]),
     array([-0.39321795]),
     array([-0.05395527]),
     array([-0.15165242]),
     array([0.24267234]),
     array([0.42220699]),
     array([0.89064644]),
     array([0.25339902]),
     array([0.35494481]),
     array([-0.04259633]),
     array([0.26653947]),
     array([0.21488204]),
     array([0.00247643]),
     array([-0.25396564]),
     array([0.03556]),
     array([-0.09819312]),
     array([0.48337445]),
     array([-0.28639891]),
     array([-0.1744417]),
     array([0.111583]),
     array([0.11000565]),
     array([0.16129092]),
     array([-0.28728283]),
     array([-0.06273703]),
     array([0.34339096]),
     array([-0.0498094]),
     array([-0.31325867]),
     array([-0.05386234]),
     array([0.01373872]),
     array([0.19155034]),
     array([-0.92644966]),
     array([0.64579777]),
     array([-0.11384351]),
     array([0.40892542]),
     array([-0.153066]),
     array([-0.19006574]),
     array([0.38850087]),
     array([0.06545069]),
     array([0.27608713]),
     array([-0.29192579]),
     array([-0.37241401]),
     array([-0.12460921]),
     array([-0.24422531]),
     array([0.41404828]),
     array([-0.66884958]),
     array([-0.17037665]),
     array([0.01446051]),
     array([-0.16891676]),
     array([-0.51293571]),
     array([0.00852009]),
     array([0.16580323]),
     array([-0.07185358]),
     array([0.46602801]),
     array([-0.08871053]),
     array([-0.31267399]),
     array([-0.40918875]),
     array([-0.45967265]),
     array([0.3819456]),
     array([-0.19219466]),
     array([0.46671616]),
     array([0.33054106]),
     array([-0.14062157]),
     array([0.03938212]),
     array([0.33692838]),
     array([-0.03576847]),
     array([0.34953852]),
     array([-0.34429731]),
     array([-0.05233571]),
     array([-0.42514462]),
     array([-0.32500558]),
     array([-0.21269587]),
     array([0.28091156]),
     array([-0.04162495]),
     array([0.18509486]),
     array([0.1624371]),
     array([-0.00746598]),
     array([0.00784622]),
     array([0.18833878]),
     array([0.17011479]),
     array([0.96753433]),
     array([0.04973765]),
     array([0.05038282]),
     array([0.23043104]),
     array([-0.27145813]),
     array([-0.06431106]),
     array([-1.36282181]),
     array([0.05989469]),
     array([-0.18050084]),
     array([0.36725877]),
     array([-0.07311184]),
     array([-0.83522728]),
     array([-0.10177917]),
     array([0.4125339]),
     array([-0.12457235]),
     array([-0.46512021]),
     array([0.03218054]),
     array([-0.09964876]),
     array([0.25817489]),
     array([0.04484203]),
     array([0.26618424]),
     array([-0.26417452]),
     array([0.12801219]),
     array([0.09813151]),
     array([0.0421007]),
     array([-0.51149196]),
     array([0.32097155]),
     array([-0.23004199]),
     array([0.33973381]),
     array([-0.42896067]),
     array([0.07246608]),
     array([-0.21984177]),
     array([-0.35819932]),
     array([0.45283863]),
     array([-0.16424184]),
     array([0.06242951]),
     array([-0.05676393]),
     array([0.5172152]),
     array([-0.71496929]),
     array([0.12064199]),
     array([-0.11997088]),
     array([-0.40060662]),
     array([0.49762798]),
     array([-0.13143379]),
     array([-0.31646419]),
     array([0.1802232]),
     array([-0.05799159]),
     array([-0.31583454]),
     array([-0.22836126]),
     array([-0.23231473]),
     array([0.16574673]),
     array([0.09099208]),
     array([0.10734312]),
     array([-0.06954393]),
     array([0.16109681]),
     array([0.54751249]),
     array([-0.32665712]),
     array([-0.0199644]),
     array([0.12099399]),
     array([-0.34541394]),
     array([-0.0593254]),
     array([0.1322016]),
     array([0.42996216]),
     array([-0.457135]),
     array([0.41822866]),
     array([0.05171805]),
     array([-0.1242461]),
     array([-0.0031598]),
     array([-0.23127847]),
     array([-0.20467694]),
     array([-0.17401629]),
     array([0.03991123]),
     array([-0.29216498]),
     array([-0.40224092]),
     array([0.22936631]),
     array([0.00919627]),
     array([-0.16350101]),
     array([-0.3484204]),
     array([0.19291053]),
     array([0.44908686]),
     array([-0.70017769]),
     array([0.16666204]),
     array([0.3007208]),
     array([0.19733504]),
     array([0.02864706]),
     array([0.08956003]),
     array([0.00222118]),
     array([-0.13253731]),
     array([-0.63837891]),
     array([0.0190715]),
     array([-0.29569653]),
     array([-0.1441581]),
     array([0.01245352]),
     array([0.38863156]),
     array([0.76879635]),
     array([0.09142519]),
     array([-0.08981965]),
     array([-0.52175225]),
     array([-0.62483692]),
     array([-0.17351034]),
     array([0.07652286]),
     array([-0.05567575]),
     array([0.04507579]),
     array([0.10163671]),
     array([0.60995908]),
     array([-0.02097538]),
     array([0.16998844]),
     array([-0.49700063]),
     array([0.34723107]),
     array([0.09047681]),
     array([-0.48374914]),
     array([0.08760173]),
     array([-0.86131689]),
     array([0.2628024]),
     array([0.13410824]),
     array([-1.54045393]),
     array([-0.37844961]),
     array([-0.08197556]),
     array([0.03672352]),
     array([-0.15045159]),
     array([-0.320254]),
     array([-0.23522503]),
     array([-0.29367482]),
     array([-0.41622814]),
     array([0.51551829]),
     array([-0.34931745]),
     array([-0.86532611]),
     array([0.26839961]),
     array([-0.19656079]),
     array([0.26015797]),
     array([-0.50028789]),
     array([-0.58461152]),
     array([0.09349557]),
     array([1.0021192]),
     array([-0.02187049]),
     array([0.05941547]),
     array([0.16434764]),
     array([0.34527563]),
     array([0.58063577]),
     array([-0.04041381]),
     array([-0.02045284]),
     array([0.12484688]),
     array([0.20296197]),
     array([0.777776]),
     array([-0.25581606]),
     array([-0.01701365]),
     array([0.20490591]),
     array([0.86520039]),
     array([0.24248242]),
     array([-0.88162228]),
     array([-0.28251489]),
     array([-0.06735514]),
     array([-0.22147807]),
     array([0.41196077]),
     array([0.0677609]),
     array([0.1201561]),
     array([-0.19238397]),
     array([0.22927849]),
     array([-0.49214509]),
     array([-0.18017768]),
     array([-0.24959393]),
     array([0.88571744]),
     array([0.06918778]),
     array([-0.21486399]),
     array([-0.21468716]),
     array([0.40672458]),
     array([-0.59384624]),
     array([-0.17074267]),
     array([0.14345601]),
     array([-0.05675037]),
     array([-0.24010601]),
     array([-0.25257519]),
     array([-0.16890519]),
     array([-0.36083852]),
     array([0.67033942]),
     array([-0.16527028]),
     array([-0.59901175]),
     array([0.43459711]),
     array([0.26819723]),
     array([-0.13087779]),
     array([-0.0751195]),
     array([-0.09944864]),
     array([0.43993741]),
     array([-0.05103737]),
     array([-0.0102798]),
     array([-0.4046511]),
     array([-0.71020906]),
     array([0.15228259]),
     array([-0.27022617]),
     array([0.58386384]),
     array([-0.04966377]),
     array([0.10546989]),
     array([0.04139659]),
     array([-0.20561776]),
     array([-0.52812481]),
     array([0.59362813]),
     array([0.05622732]),
     array([-0.07278211]),
     array([0.23172333]),
     array([-0.13304765]),
     array([-0.36387993]),
     array([-0.89246869]),
     array([0.2984485]),
     array([0.26929636]),
     array([0.77868591]),
     array([-0.00074986]),
     array([-0.24364702]),
     array([0.52690711]),
     array([0.54230013]),
     array([0.11456749]),
     array([-0.03648697]),
     array([-0.27886792]),
     array([-0.12844968]),
     array([-0.16107733]),
     array([0.58471587]),
     array([0.02632334]),
     array([0.07157142]),
     array([-0.04133918]),
     array([0.05604101]),
     array([-0.02979177]),
     array([-0.76802764]),
     array([0.3278804]),
     array([-0.36863623]),
     array([-1.30724938]),
     array([-0.6937609]),
     array([-0.00518274]),
     array([0.1187104]),
     array([-0.00434209]),
     array([-0.18214605]),
     array([-0.69015679]),
     array([0.18374246]),
     array([-0.12566842]),
     array([0.12689861]),
     array([-0.13766775]),
     array([-0.12837235]),
     array([-0.43692597]),
     array([-0.10291216]),
     array([0.23866396]),
     array([0.24775889]),
     array([0.11783816]),
     array([-0.08986773]),
     array([0.61186612]),
     array([-0.26143157]),
     array([-0.24287007]),
     array([-0.04531501]),
     array([-0.09630434]),
     array([-0.6339032]),
     array([0.09084901]),
     array([-0.13885515]),
     array([-0.3388086]),
     array([0.01697549]),
     array([0.24953841]),
     array([-0.49189166]),
     array([0.0220324]),
     array([0.04858652]),
     array([-0.35023241]),
     array([0.46928002]),
     array([0.26287265]),
     array([0.73762192]),
     array([0.11887366]),
     array([-0.67210492]),
     array([0.15471876]),
     array([-0.00245061]),
     array([0.35748821]),
     array([-0.06203624]),
     array([-0.22274291]),
     array([0.09212767]),
     array([-0.25354311]),
     array([0.18548067]),
     array([0.505561]),
     array([-0.13100313]),
     array([0.03869254]),
     array([-0.10256656]),
     array([-0.18251721]),
     array([-0.24240573]),
     array([0.40452897]),
     array([-0.09661389]),
     array([0.18474103]),
     array([-0.06527904]),
     array([0.09150998]),
     array([0.50012131]),
     array([0.17988869]),
     array([-0.25677166]),
     array([0.43052139]),
     array([-0.10907433]),
     array([0.07281288]),
     array([-0.71123144]),
     array([-0.26057571]),
     array([0.28672215]),
     array([0.07066436]),
     array([0.32761781]),
     array([-0.03022278]),
     array([0.48732311]),
     array([0.24329168]),
     array([-0.07618235]),
     array([-0.27158278]),
     array([0.09950069]),
     array([-0.66674907]),
     array([0.23708201]),
     array([0.06625114]),
     array([-0.11101232]),
     array([0.61064901]),
     array([-0.71601969]),
     array([-0.29855496]),
     array([0.17883796]),
     array([0.14081261]),
     array([-0.17122483]),
     array([0.15490203]),
     array([0.00908581]),
     array([-0.13654823]),
     array([-0.12633988]),
     array([-0.21457368]),
     array([-0.52265382]),
     array([-0.63946154]),
     array([0.05026781]),
     array([0.10901516]),
     array([0.2242826]),
     array([-0.22477308]),
     array([-0.01103039]),
     array([-0.35534072]),
     array([0.06363799]),
     array([-0.19248995]),
     array([-0.60993993]),
     array([-0.06543289]),
     array([0.16055283]),
     array([0.07127491]),
     array([0.08479667]),
     array([0.3556288]),
     array([0.14814134]),
     array([-0.11288904]),
     array([0.06367305]),
     array([-0.07084012]),
     array([0.02129453]),
     array([0.15216129]),
     array([0.8960252]),
     array([-0.22953677]),
     array([-0.18316518]),
     array([-0.27156441]),
     array([-0.31273]),
     array([-0.06861415]),
     array([-0.25200784]),
     array([0.17858588]),
     array([0.17751785]),
     array([-0.31362684]),
     array([0.19464842]),
     array([-0.61554665]),
     array([0.1078479]),
     array([-0.26172322]),
     array([0.3664344]),
     array([-0.27207201]),
     array([0.27394972]),
     array([-0.05850541]),
     array([0.11395478]),
     array([-0.23281689]),
     array([0.62318763]),
     array([-0.20947377]),
     array([0.02122955]),
     array([0.34378341]),
     array([-0.00563057]),
     array([0.14918525]),
     array([0.0470106]),
     array([-0.48737926]),
     array([-0.48320354]),
     array([-0.21687067]),
     array([-0.1549662]),
     array([-0.03044837]),
     array([-0.13966073]),
     array([-0.06307361]),
     array([0.47503526]),
     array([0.05154704]),
     array([-0.32348865]),
     array([0.04293028]),
     array([0.05001677]),
     array([0.34926555]),
     array([0.27779965]),
     array([0.60761514]),
     array([-0.2883191]),
     array([0.55919513]),
     array([0.06039164]),
     array([-0.24983148]),
     array([-0.64318871]),
     array([-0.44385773]),
     array([-0.25551456]),
     array([-0.09630977]),
     array([1.1315565]),
     array([0.15793769]),
     array([0.33305118]),
     array([0.05534194]),
     array([-0.04266218]),
     array([-0.24749155]),
     array([0.44199648]),
     array([0.21749294]),
     array([-0.32068151]),
     array([-0.35128608]),
     array([-0.33119766]),
     array([0.11311468]),
     array([-0.0291709]),
     array([-0.19190174]),
     array([-0.30168387]),
     array([-0.23758649]),
     array([-0.11920054]),
     array([0.13170275]),
     array([-0.16861576]),
     array([0.29745457]),
     array([0.02548287]),
     array([0.02761211]),
     array([0.86811091])]

```python
# yprime = np.array(yprime)
data = np.concatenate([x1,x2,y], axis = 1)
df = pd.DataFrame(data, columns=['X1','X2','Y'])
```

```python
simulated_X = df[['X1','X2']]
simulated_y = df.Y

simulated_X.to_csv("../data/processed/simulated_lee/simulated_X",index=False)
simulated_y.to_csv("../data/processed/simulated_lee/simulated_y",index=False)
```

```python
simulated_X
```

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>X1</th>
      <th>X2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.103129</td>
      <td>-0.359429</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1.498644</td>
      <td>-0.158249</td>
    </tr>
    <tr>
      <th>2</th>
      <td>0.450521</td>
      <td>0.732279</td>
    </tr>
    <tr>
      <th>3</th>
      <td>-0.896077</td>
      <td>0.805959</td>
    </tr>
    <tr>
      <th>4</th>
      <td>1.980726</td>
      <td>-0.735985</td>
    </tr>
    <tr>
      <th>...</th>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <th>995</th>
      <td>0.172554</td>
      <td>0.747242</td>
    </tr>
    <tr>
      <th>996</th>
      <td>1.151053</td>
      <td>0.862832</td>
    </tr>
    <tr>
      <th>997</th>
      <td>0.050584</td>
      <td>-0.008455</td>
    </tr>
    <tr>
      <th>998</th>
      <td>1.871526</td>
      <td>0.943660</td>
    </tr>
    <tr>
      <th>999</th>
      <td>1.896322</td>
      <td>-0.338582</td>
    </tr>
  </tbody>
</table>
<p>1000 rows × 2 columns</p>
</div>

```python

```


```python
import numpy as np
import matplotlib.pyplot as plt
from IPython.core.display import display

plt.style.use('JGW')
import pandas as pd
```

```python
# solve Equation 13 from Prelab E2 for E_cell
import sympy as sp
from sympy.interactive import printing
printing.init_printing(use_latex='mathjax')

E, R, T, F, n_Fe2, n_Ce4, E0_Fe, E0_Ce = sp.symbols('E_{Cell} R T F n_{Fe(II)init} n_{C(IV)add} E^\circ_{Fe^{3/2}} E^\circ_{Fe^{4/3}}')
# display(E, R, T, F, n_Fe2, n_Ce4, E0_Fe, E0_Ce)
def f(x):
    return (1 + sp.exp(-x))**(-1)

def g(x):
    return 1+sp.exp(x)

eq13 = sp.Eq(n_Ce4, n_Fe2 * f((E - E0_Fe)/(R*T/F)) * g((E - E0_Ce)/(R*T/F)))
display(eq13)

E_predict = sp.solve(eq13, E)
print('Solving for E_Cell:', '\n')
display(E_predict)
```

$\displaystyle n_{C(IV)add} = \frac{n_{Fe(II)init} \left(e^{\frac{F \left(- E^\circ_{Fe^{4/3}} + E_{Cell}\right)}{R T}} + 1\right)}{1 + e^{- \frac{F \left(- E^\circ_{Fe^{3/2}} + E_{Cell}\right)}{R T}}}$

    Solving for E_Cell:

$\displaystyle \left[ \frac{R T \log{\left(\frac{- \sqrt{\left(n_{C(IV)add}^{2} e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}} + 4 n_{C(IV)add} n_{Fe(II)init} e^{\frac{E^\circ_{Fe^{3/2}} F}{R T}} - 2 n_{C(IV)add} n_{Fe(II)init} e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}} + n_{Fe(II)init}^{2} e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}}\right) e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}}} + \left(n_{C(IV)add} - n_{Fe(II)init}\right) e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}}}{2 n_{Fe(II)init}} \right)}}{F}, \  \frac{R T \log{\left(\frac{\sqrt{\left(n_{C(IV)add}^{2} e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}} + 4 n_{C(IV)add} n_{Fe(II)init} e^{\frac{E^\circ_{Fe^{3/2}} F}{R T}} - 2 n_{C(IV)add} n_{Fe(II)init} e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}} + n_{Fe(II)init}^{2} e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}}\right) e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}}} + \left(n_{C(IV)add} - n_{Fe(II)init}\right) e^{\frac{E^\circ_{Fe^{4/3}} F}{R T}}}{2 n_{Fe(II)init}} \right)}}{F}\right]$

```python
# Collect known parameters
known_params = {R: 8.314,
                F: 96485,
                T: 298}

# The first solution gives only complex numbers (log(negative)), toss it
model = E_predict[1].subs(known_params)
display(model)
```

$\displaystyle 0.0256783126910919 \log{\left(\frac{\sqrt{\left(n_{C(IV)add}^{2} e^{38.9433687497276 E^\circ_{Fe^{4/3}}} + 4 n_{C(IV)add} n_{Fe(II)init} e^{38.9433687497276 E^\circ_{Fe^{3/2}}} - 2 n_{C(IV)add} n_{Fe(II)init} e^{38.9433687497276 E^\circ_{Fe^{4/3}}} + n_{Fe(II)init}^{2} e^{38.9433687497276 E^\circ_{Fe^{4/3}}}\right) e^{38.9433687497276 E^\circ_{Fe^{4/3}}}} + \left(n_{C(IV)add} - n_{Fe(II)init}\right) e^{38.9433687497276 E^\circ_{Fe^{4/3}}}}{2 n_{Fe(II)init}} \right)}$

```python
# Get the data
data_path = r"C:\Users\jgage\OneDrive - Stanford\2021\CHEM 274\Labs\E3\Fine Titration Data.txt"
number_steps_in_calibration = 6000
mass_of_calibration_in_g = 1.355

titration_data = pd.read_csv(data_path, skiprows = 4, sep = ',')
titration_data['step in g sol'] = (titration_data['step'] / number_steps_in_calibration) * mass_of_calibration_in_g
titration_data['step in mol Ce(IV)'] = titration_data['step in g sol'] * (0.990 / 20.030) / 548.22

x_data = titration_data['step in mol Ce(IV)']
y_data = titration_data['mean potential']
```

```python
guesses = {n_Fe2: 1.40 * 10**-4,
           E0_Fe: 0.47,
           E0_Ce: 1.23
}

y_model = []
for step in range(len(x_data)):
    y_model.append(model.subs(guesses).subs(n_Ce4, x_data[step]))

print(y_model)
```

    [zoo, 0.361706950752987, 0.380139865423610, 0.390759447951217, 0.398557499931513, 0.404530847968344, 0.409771672347333, 0.414122044628858, 0.417913087970662, 0.421404808862530, 0.427372233276485, 0.432579349023674, 0.437247511982658, 0.447111347032517, 0.455615897852612, 0.463413949832908, 0.470929211695427, 0.478463125772971, 0.481552914950427, 0.484753758313137, 0.488067177836059, 0.491518266438717, 0.495176068626863, 0.499062039988369, 0.503307077455659, 0.505568199198108, 0.506747815289365, 0.507961511505766, 0.509239235036616, 0.511858205632664, 0.514675215113004, 0.517767603772799, 0.524863469622043, 0.533947765324265, 0.547035334566760, 0.572191979880249, 1.15523120547763, 1.17703054453523, 1.18864824595946, 1.20269102629562]


# UCL AI Society Machine Learning Tutorials
### Session 01. Introduction to Numpy, Pandas and Matplotlib

### Contents
1. Numpy
2. Pandas
3. Matplotlib
4. EDA(Exploratory Data Analysis)

### Aim
At the end of this session, you will be able to:
- Understand the basics of numpy.
- Understand the basics of pandas.
- Understand the basics of matplotlib.
- Perform a simple EDA using libraries above.

## 3. Matplotlib
Matplotlib is a Python data visualisation library. Its plotting system is similar to that of MATLAB.

### 3.1 Basics of Matplotlib

```python
# run this cell if you haven't installed matplotlib
!pip install matplotlib
```

    Requirement already satisfied: matplotlib in d:\anaconda3\envs\py36\lib\site-packages (3.3.2)
    Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in d:\anaconda3\envs\py36\lib\site-packages (from matplotlib) (2.4.7)
    Requirement already satisfied: cycler>=0.10 in d:\anaconda3\envs\py36\lib\site-packages (from matplotlib) (0.10.0)
    Requirement already satisfied: python-dateutil>=2.1 in d:\anaconda3\envs\py36\lib\site-packages (from matplotlib) (2.8.1)
    Requirement already satisfied: numpy>=1.15 in c:\users\mao jianqiao\appdata\roaming\python\python36\site-packages (from matplotlib) (1.18.5)
    Requirement already satisfied: kiwisolver>=1.0.1 in d:\anaconda3\envs\py36\lib\site-packages (from matplotlib) (1.2.0)
    Requirement already satisfied: certifi>=2020.06.20 in d:\anaconda3\envs\py36\lib\site-packages (from matplotlib) (2020.6.20)
    Requirement already satisfied: pillow>=6.2.0 in d:\anaconda3\envs\py36\lib\site-packages (from matplotlib) (7.2.0)
    Requirement already satisfied: six in c:\users\mao jianqiao\appdata\roaming\python\python36\site-packages (from cycler>=0.10->matplotlib) (1.15.0)

- `%matplotlib inline` is only available for Jupyter Notebook and Jupyter QtConsole. With this backend, the output of your command will be displayed inline with frontends, directly below the code cell that produces it.
- `%matplotlib tk` is also only available for Jupyter Notebook. The output of your command will be displayed on a new broswer.

```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
# %matplotlib tk
```

Plot a **y=sin(x)** graph.

```python
# declare x and y
x = np.arange(0, 3 * np.pi, 0.1)
y = np.sin(x)
print(x)
print(y)
assert len(x)==len(y)
```

    [0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.  1.1 1.2 1.3 1.4 1.5 1.6 1.7
     1.8 1.9 2.  2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.  3.1 3.2 3.3 3.4 3.5
     3.6 3.7 3.8 3.9 4.  4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5.  5.1 5.2 5.3
     5.4 5.5 5.6 5.7 5.8 5.9 6.  6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7.  7.1
     7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 8.  8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9
     9.  9.1 9.2 9.3 9.4]
    [ 0.          0.09983342  0.19866933  0.29552021  0.38941834  0.47942554
      0.56464247  0.64421769  0.71735609  0.78332691  0.84147098  0.89120736
      0.93203909  0.96355819  0.98544973  0.99749499  0.9995736   0.99166481
      0.97384763  0.94630009  0.90929743  0.86320937  0.8084964   0.74570521
      0.67546318  0.59847214  0.51550137  0.42737988  0.33498815  0.23924933
      0.14112001  0.04158066 -0.05837414 -0.15774569 -0.2555411  -0.35078323
     -0.44252044 -0.52983614 -0.61185789 -0.68776616 -0.7568025  -0.81827711
     -0.87157577 -0.91616594 -0.95160207 -0.97753012 -0.993691   -0.99992326
     -0.99616461 -0.98245261 -0.95892427 -0.92581468 -0.88345466 -0.83226744
     -0.77276449 -0.70554033 -0.63126664 -0.55068554 -0.46460218 -0.37387666
     -0.2794155  -0.1821625  -0.0830894   0.0168139   0.1165492   0.21511999
      0.31154136  0.40484992  0.49411335  0.57843976  0.6569866   0.72896904
      0.79366786  0.85043662  0.8987081   0.93799998  0.96791967  0.98816823
      0.99854335  0.99894134  0.98935825  0.96988981  0.94073056  0.90217183
      0.85459891  0.79848711  0.7343971   0.66296923  0.58491719  0.50102086
      0.41211849  0.31909836  0.22288991  0.12445442  0.02477543]

```python
# plot a graph of sin(x)
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.title('y = sin(x)')
plt.show()
# plt.savefig('./image/sinGraph.png')
```

Plot multiple functions: **y = x**, **y = x^2**, and **y = x^3** in one graph.

```python
# plot multiple graphs in one graph
x = np.arange(10)
x_linear = x
x_square = x ** 2
x_cubic = x ** 3

plt.plot(x, x_linear)
plt.plot(x, x_square)
plt.plot(x, x_cubic)
plt.xlabel('x axis')
plt.ylabel('y axis')
plt.grid()
plt.legend(['y = x', 'y = x^2', 'y = x^3'])
plt.title('y = x | y = x^2 | y = x^3')
# TODO: print out the graph under this cell
plt.show()
```

### 3.2 Various Types of Plots
Matplotlib library supports various types of graphs such as bar graph, histogram, scatter plot, area plot and pie plot. Let's use IMDB-Movie-Data again to get a better understanding of the data. Visualising data is a crucial part of EDA, which you'll get hands-on experience soon!

```python
movie = pd.read_csv("./data/IMDB-Movie-Data.csv")
movie.columns
```

    Index(['Rank', 'Title', 'Genre', 'Description', 'Director', 'Actors', 'Year',
           'Runtime (Minutes)', 'Rating', 'Votes', 'Revenue (Millions)',
           'Metascore'],
          dtype='object')

Let's see if there is a positive correlation between `Rating` and `Revenue (Millions)` using `scatter` function. Parameter `s` controls the diameter / size of the scattered dots and `alpha` controls the degree of their transparency.

#### 3.2.1 Scatter plot

```python
movie.plot.scatter(x = 'Rating', y = 'Revenue (Millions)', s = 10, alpha = 1)
```

They seem to have a bit of positive relationship.

Now, let's try to use different colors and markers to distinguish different groups of data and their centroids.

```python
# Read the dataset and print the first 20 samples.
scatters = pd.read_csv("./data/scatters_data.csv",index_col=0)
scatters['class']=list(map(int,scatters['class']))
print(scatters.head(20))

# Split the datasets according to class index
clas=[0,1,2]
for clsNum in clas:
    tempData= scatters[scatters['class'].isin([clsNum])]
    exec("class%s=tempData"%clsNum)
# Store them into a dictionary
class_dict={'0':class0,'1':class1,'2':class2}
```

            x     y  class
    0    1.00   3.1      2
    1    2.00   2.1      2
    2    3.00   0.4      2
    3    8.00  10.1      1
    4    9.00   9.9      1
    5   11.00   7.3      1
    6    6.60   7.4      1
    7    3.30   5.7      2
    8    2.30   3.3      2
    9    4.40   5.7      2
    10   7.10   9.1      1
    11   9.10   6.8      1
    12  12.30   6.1      1
    13   0.78   4.1      2
    14   1.56   4.2      2
    15  10.00  12.0      1
    16   2.00   7.8      2
    17  11.00  10.5      1
    18   2.00  16.0      0
    19   3.00  14.0      0

Firstly, calculate centroids for each class. Centroid ($c_x$,$c_y$) of a set of equally weighted points in 2D space is simply given by:

\begin{align}
\begin{array}
\$c_x  = \frac{\sum_{i=1}^n{x_i}}{n}
\end{array} ,
\begin{array}
\$c_y  = \frac{\sum_{i=1}^n{y_i}}{n}
\end{array}
\end{align}

```python
# TODO: Calculate Cx, Cy for each class
cx=[np.mean(class0['x']),np.mean(class1['x']),np.mean(class2['x'])] # x coordinates for centroids of class 0, 1 and 2
cy=[np.mean(class0['y']),np.mean(class1['y']),np.mean(class2['y'])] # y coordinates for centroids of class 0, 1 and 2
```

Let's plot scatters for data points by assigning different color and marker for different group. And then plot their centroids with corresponding color but same marker **X**.

```python
# Set potential colors and markers
color=['r','g','b']
marker=['o','s','v']
# TODO: plot scatters for data points and their centroids.
for i in clas:
    #plot the groups of data
    plt.scatter(class_dict[str(i)]['x'],class_dict[str(i)]['y'],color=color[i],marker=marker[i],s=100)
    #plot centroid of each group you calculate above
    plt.scatter(cx[i],cy[i],color=color[i],marker='x',s=200)
```

#### 3.2.2 Bar plot
Let's look into the Most Used Programming, Scripting, and Markup Languages in 2018.
Use `plt.bar` and `plt.xticks` to plot a 'language against Percentage' graph.

`plt.xticks` has two parameters: `ticks` accepts an array to set the space between scales, `labels` accepts an arary or a list to indicate the name of each scale.

```python
# https://insights.stackoverflow.com/survey/2018#most-popular-technologies
language = ["JS", "HTML", "SQL", "Java", "Shell", "Python", "C#"] # Name of each scale
percentage = [69.8, 68.5, 57.0, 45.3, 39.8, 38.8, 34.4]

# Generating the x positions as the scales on x axis.
x_positions = np.array(range(len(percentage)))

# TODO: Create a bar plot
plt.bar(x_positions, percentage)
plt.xticks(x_positions, language)
plt.xlabel('language')
plt.ylabel('Percentage (%)')
plt.title("Most Used Programming, Scripting, and Markup Languages in 2018")
plt.show()
```

Then, let's learn how to adjust the width of the bars.
`plt.bar()` has a parameter called `width` which can control the width of the bars and between each bar.

```python
# Set the bar width(any float between 0 and 1)
bar_width=0.5
# TODO: Create a bar plot with different bar width
plt.bar(x_positions, percentage,width=bar_width)
plt.xticks(x_positions, language)
plt.xlabel('language')
plt.ylabel('Percentage (%)')
plt.title("Most Used Programming, Scripting, and Markup Languages in 2018")
plt.show()

# TODO: what if the bar_width is set bigger than 1?
bar_width=1.5
plt.bar(x_positions, percentage,width=bar_width)
plt.xticks(x_positions, language)
plt.xlabel('language')
plt.ylabel('Percentage (%)')
plt.title("Most Used Programming, Scripting, and Markup Languages in 2018")
plt.show()
# Answer:Overlapping may happen, where lower bars are overlapped by higher bars!
```

Bar charts with multiple groups are particularly useful. Let's compare the Most Used Programming, Scripting, and Markup Languages in 2020 with that in 2018.
On this occasion, we are going to add labels, grids and each bar's value. Mind that the attributes used for `AxeSubplot` object may partially differ from these in the previous cases of directly using `pyplot` module.

`set_xticks()` is to set the ticks' position on x axis.

`set_xticklabels()` maps elements in `language` to each tick position.

`yaxis.grid()` sets the grids in y direction

`legend()` places a legend, where the parameter `loc='best'` indicates that the location of the legend card is determined automatically.

`annotate()` add text/annotation in the chart. Parameter `test` is the annotation content, `xy` is position of annotating point and `xytext` is the postion of annotation content.

```python
# language = ["JS", "HTML", "SQL", "Java", "Shell", "Python", "C#"]

# The first group (2018) of language popularity
percentage_2018 = percentage
# The second group (2020) of language popularity
percentage_2020 = [67.8, 63.5, 54.4, 41.1, 36.6, 41.7, 31.0]

fig,ax=plt.subplots()
# set bar width to 0.4, then it gives 0.1 bar padding.
barwidth=0.4
# Plot the first group of data
ax.bar(x_positions, percentage_2018, width=barwidth, label='2018')
# Plot the second group of data
ax.bar(x_positions+barwidth, percentage_2020, width=barwidth, label='2020')

# TODO: set proper ticks, labels, grids, and bars' value
ax.set_xticks(ticks= x_positions+0.5*barwidth) # Set the ticks in the center of the two bars
ax.set_xticklabels(language)
ax.set_yticks(ticks=range(0,101,10)) # Set the ticks on y axis by 10% spacing
ax.yaxis.grid(True)

# add the value of each bar
for xy in zip(x_positions, percentage_2018):
    ax.annotate(text="%s" % xy[1], xy=xy, xytext=(-15, 10), textcoords='offset points')
for xy in zip(x_positions+barwidth, percentage_2020):
    ax.annotate(text="%s" % xy[1], xy=xy, xytext=(-10, 10), textcoords='offset points')
ax.legend(loc='best')
ax.set_xlabel('language')
ax.set_ylabel('Percentage (%)')
fig.suptitle("Comparison of Most Used Programming Languages in 2018 and 2020")
```

It is often useful to use horizontal bars, particularly if there are a lot of them. The transformation from vertical chart to horizontal chart only requires replacing `bar` method with its horizontal counterpart `barh`, and simply switch the axis labels, grids, etc.

```python
# TODO: Rewrite the counterpart horizontal bar chart by referring to the previous case.
y_positions=np.array(range(len(percentage)))
fig,ax=plt.subplots()
# Very similar to the previous example
# Plot the first group of data
ax.barh(y_positions,percentage_2018, barwidth, label='2018')
# Plot the second group of data
ax.barh(y_positions+barwidth,percentage_2020, barwidth, label='2020')
ax.set_yticks(ticks= x_positions+0.5*barwidth)
ax.set_yticklabels(language)
ax.set_xticks(range(0,101,10))
ax.xaxis.grid(True)
for xy in zip(percentage_2018,y_positions):
    ax.annotate(text="%s" % xy[0], xy=xy, xytext=(5, -3), textcoords='offset points')
for xy in zip(percentage_2020,y_positions+barwidth):
    ax.annotate(text="%s" % xy[0], xy=xy, xytext=(5, -3), textcoords='offset points')
ax.legend(loc='best')
ax.set_ylabel('language')
ax.set_xlabel('Percentage (%)')
fig.suptitle("Comparison of Most Used Programming Languages in 2018 and 2020")
```

### 3.3 (Advanced) Matplotlib Exercise

```python
# TODO: Draw 4 graphs in total in one figure
# Hint: https://matplotlib.org/api/_as_gen/matplotlib.pyplot.subplots.html
# You can see great example code from the url above

fig, axes = plt.subplots(2, 2)

# TODO: Scatter Graph
x = np.random.randn(50)
y = np.random.randn(50)
colors = np.random.randint(0, 100, 50)
sizes = 500 * np.pi * np.random.rand(50) ** 2
axes[0, 0].scatter(x, y, c=colors, s=sizes, alpha=0.3)

# TODO: Bar Graph
x = np.arange(10)
axes[0, 1].bar(x, x ** 2)

# TODO: Multi-Bar Graph
x = np.random.rand(3)
y = np.random.rand(3)
z = np.random.rand(3)
data = [x, y, z]

x_ax = np.arange(3)
for i in x_ax:
    axes[1, 0].bar(x_ax, data[i], bottom=np.sum(data[:i], axis=0))
axes[1, 0].set_xticks(x_ax)
axes[1, 0].set_xticklabels(['A', 'B', 'C'])

# TODO: Histogram Graph
data = np.random.randn(1000)
axes[1][1].hist(data, bins=40)

# TODO: Show the image and save it to png file
plt.show()
# plt.savefig("sample1.png")
```

### What to do next?
Below websites would be helpful for your further study on matplotlib:
- [DataCamp Matplotlib Tutorial: Python Plotting](https://www.datacamp.com/community/tutorials/matplotlib-tutorial-python)
- [Matplotlib official website](https://matplotlib.org/#)
- [Python Plotting With Matplotlib (Guide)](https://realpython.com/python-matplotlib-guide/)
- [Different plotting using pandas and matplotlib](https://www.geeksforgeeks.org/different-plotting-using-pandas-and-matplotlib/)
- [Matplotlib tutorial for beginner](https://github.com/rougier/matplotlib-tutorial)


```python
from pycalphad.core.utils import NumPyPrinter
from sympy import Piecewise, lambdify, symbols, Integer,  Pow
from sympy.printing.lambdarepr import LambdaPrinter
import numpy as np
x, y = symbols('x y')
```

```python
class MyNumPyPrinter(NumPyPrinter):
    def _print_Piecewise(self, expr):
        return LambdaPrinter._print_Piecewise(self, expr)
    def _print_Integer(self, expr):
        # Upcast to work around an integer overflow bug
        return 'asarray({0}, dtype=float)'.format(expr)
p = Piecewise((1/x, y < -1), (Pow(Integer(50), Integer(10e40), evaluate=False), y <= 1), (1/x, True))
lambdify([x, y], p, modules='numpy', printer=NumPyPrinter)(50, 0)
```

```python
NumPyPrinter().doprint(p)
```

```python
Pow(Integer(50), Integer(10e40), evaluate=False)
```

```python

```


## 2-2. アダマールテスト

最も簡単な量子アルゴリズムとして、アダマールテストとよばれる以下のような量子回路（図1）を考える。つまり、第1ビットは$|0\rangle$に、第2ビット以降は状態$|\psi\rangle$に初期化されていて、まず第1ビットにアダマールゲートをかける。そして、全体に制御ユニタリ演算子$\Lambda(U)$（後述）を作用させ、再び第1ビットにアダマールゲートをかけて、最後にその第1ビットを測定する。

ここで制御ユニタリ演算子$\Lambda(U)$というのは、第1量子ビットが$|0\rangle$の場合にはなにもせず、$|1\rangle$の場合には$U$を作用させるユニタリ演算である。

$$
\Lambda (U) = |0\rangle \langle 0| \otimes I + |1\rangle \langle 1|  \otimes U.
$$

つまり、１つ目の量子ビットが$|0\rangle$か$|1\rangle$かによって条件分岐して、「なにもしない」または「$U$を作用させる」という演算が実行される。従来のコンピュータでは条件分岐は同時に実行することができないが、量子コンピュータでは状態の重ね合わせを利用して、条件分岐を**同時並列的に実行することができる**。

このアダマールテストの動作について考えていく。最初は簡単のために、量子状態$|\psi \rangle$が
ユニタリー演算（行列）$U$の固有値$e^{i \lambda}$の固有状態（固有ベクトル）である場合を考える：
\begin{eqnarray}
U|\psi \rangle = e^{i \lambda} |\psi\rangle.
\end{eqnarray}

１つ目の量子ビットにアダマール演算$H$を作用させることで
\begin{eqnarray}
\frac{1}{\sqrt{2}} (|0\rangle  + |1\rangle) \otimes |\psi  \rangle
\end{eqnarray}
が得られる。
その後、制御$U$演算を作用させることによって、
**固有値**$e^{i\lambda}$**が１つめの量子ビットの相対位相として得られる**（このことを**位相キックバック**と呼ぶ）:
\begin{eqnarray}
&&\frac{1}{\sqrt{2}} (|0\rangle \otimes |\psi  \rangle
+ |1\rangle \otimes U|\psi \rangle )
\\
&=&\frac{1}{\sqrt{2}} (|0\rangle \otimes |\psi  \rangle
+e^{i \lambda} |1\rangle \otimes |\psi \rangle )
\\
&=&
\frac{1}{\sqrt{2}} (|0\rangle
+e^{i \lambda} |1\rangle )\otimes |\psi  \rangle.
\end{eqnarray}
最後に、１つ目の量子ビットに再度アダマール演算を行い
\begin{eqnarray}
\left(\frac{1+e^{i\lambda}}{2}|0\rangle
+\frac{1-e^{i\lambda}}{2} |1\rangle \right)\otimes |\psi  \rangle
\label{eq01}
\end{eqnarray}
が得られる。
１つ目の量子ビットを測定すると測定結果$m=0,1$を得る確率は
\begin{eqnarray}
p_{m}=\left|\frac{1+(-1)^m e^{i\lambda}}{2}\right|^2 =\frac{1+(-1)^m \cos \lambda}{2}
\end{eqnarray}
となる。
$|\psi \rangle$、$U$、$\langle \psi |$は
それぞれ$2^n$次元の列ベクトル、$2^n \times 2^n$行列、
$2^n$次元の行ベクトルなので、
このアダマールテストを古典コンピュータ上で愚直に計算すると
指数的に大きなメモリーの確保と演算回数が必要になる。
一方で、量子コンピューターでは、
確率分布$p_m$のもとで$m$がサンプルされる。
$\cos \lambda$を
ある誤差$\epsilon$で推定したい場合は、
その逆数$1/\epsilon$の多項式回程度サンプルすればよいことになる。

同じ計算を、必ずしも固有ベクトルとは限らない、一般の入力に対して行うと、測定前の状態は、

$$
 |0\rangle \frac{I+U}{2} |\psi \rangle  +  |1\rangle  \frac{I-U}{2} |\psi \rangle
$$

となり、0もしくは1が得られる確率は、

\begin{align}
p_0 &= \frac{1+ {\rm Re} \langle \psi | U | \psi \rangle }{2} \\
p_1 &= \frac{1- {\rm Re} \langle \psi | U | \psi \rangle }{2} \tag{1}
\end{align}

となる。つまり、量子コンピュータ上でアダマールテストを実行すれば、その測定結果のサンプル平均をとることで**ベクトル**$|\psi \rangle$**でユニタリ行列**$U$**を挟んだ値を推定することができる**。同じ値を古典コンピュータで求めようとした場合、量子ビット数$n$が大きくなるにつれベクトルや行列の次元は指数的に大きくなるので、指数的な時間を要する。

なお、１つ目の量子ビットを測定した後の、２つ目の量子ビットの状態は、測定結果$m = 0, 1$に応じて以下の状態になる（規格化因子は省略）：

$$
|\psi_0\rangle = \frac{I + U}{2}|\psi\rangle,
\quad
|\psi_1\rangle = \frac{I - U}{2}|\psi\rangle.
$$

ここで、$U$が１量子ビットのユニタリ演算で、かつその固有値が$\pm 1$であるような場合を考える。固有値$\pm 1$に対応する固有ベクトル$|u_1\rangle$, $|u_{-1}\rangle$を使って$|\psi\rangle = c_1|u_1\rangle + c_2|u_2\rangle$と展開し代入することで、測定後の状態$|\psi_0\rangle$, $|\psi_1\rangle$はそれぞれ固有値$\pm 1$に対応する固有状態であることが分かる。固有値が$\pm 1$ではない場合も、アダマールテストの出力を入力として繰り返すと$U$の固有状態に状態が収束していく（興味のある人は、以下の例を参考にして試してもらいたい）。

### SymPyでの実装
具体的な例として、$U=H$（アダマールゲート）の場合を考えてみよう。補助量子ビットを$|0\rangle$、アダマールテストの入力$|\psi\rangle$も$|0\rangle$とする。

```python
from sympy import *
from sympy.physics.quantum import *
from sympy.physics.quantum.qubit import Qubit,QubitBra
init_printing() # ベクトルや行列を綺麗に表示するため
from sympy.physics.quantum.gate import X,Y,Z,H,S,T,CNOT,SWAP,CPHASE,CGateS
```

```python
# Google Colaboratory上でのみ実行してください
from IPython.display import HTML
def setup_mathjax():
    display(HTML('''

    '''))
get_ipython().events.register('pre_run_cell', setup_mathjax)
```

```python
state = Qubit('00')
```

制御H演算は、`CGateS()`を用いて

```python
ctrlH = CGateS(1,H(0))
represent(ctrlH,nqubits=2)
```

$$\left[\begin{matrix}1 & 0 & 0 & 0\\0 & 1 & 0 & 0\\0 & 0 & \frac{\sqrt{2}}{2} & \frac{1}{\sqrt{2}}\\0 & 0 & \frac{1}{\sqrt{2}} & - \frac{\sqrt{2}}{2}\end{matrix}\right]$$

と行列表示される。
測定前の状態は、

```python
H(1)*ctrlH*H(1)*state
```

とかけるが、SymPyに計算させてみると

```python
qapply(H(1)*ctrlH*H(1)*state)
```

となる。第１章で紹介したSymPyの`measure_partial`関数を用いて、1つ目の量子ビットの測定結果が0だった場合の量子状態と確率を求めると、

```python
from sympy.physics.quantum.qubit import measure_all, measure_partial, measure_all_oneshot, measure_partial_oneshot
measured_state_and_probability_zero = measure_partial(qapply(H(1)*ctrlH*H(1)*state),(1,))[0]
simplify(measured_state_and_probability_zero)
```

となる。$\langle 0 | H | 0\rangle = 1/\sqrt{2}$だから、測定確率が式(1)と一致していることが分かる。
また、２つめの量子ビットに$H$を作用させてみると、測定後の状態が$H$の固有ベクトルになっていることが分かる（SymPyのインデックスは左から１つ目が1、２つ目が0になるよう対応させていることに注意）。

```python
measured_state_zero = measured_state_and_probability_zero[0]
simplify(qapply(H(0)*measured_state_zero))
```

同様に1の測定結果を得た場合は、固有値−1の固有状態であることも確認できるので試してもらいたい。

```python
measured_state_one = measure_partial(qapply(H(1)*ctrlH*H(1)*state),(1,))[1][0]
simplify(qapply(H(0)*measured_state_one))
```