Ejemplo n.º 1
0
def FindMinimumInversePower(foldname):
    """ Finds the true minimum corresponding:
        Note Gradient Descent step should be adequately small for this to work
    """
    foldpath = BASE_DIRECTORY + "/" + foldname
    sysparams = load_params(foldpath)
    (hs_radii, initial_coords, box_length) = load_secondary_params(foldpath)
    box_length = float(box_length)
    boxv = [box_length] * sysparams.ndim.value
    potential = InversePower(
        sysparams.power.value,
        sysparams.eps.value,
        use_cell_lists=False,
        ndim=sysparams.ndim.value,
        radii=hs_radii * 1.0,
        boxvec=boxv,
    )
    print(box_length, "box_length")
    print(len(initial_coords))
    # initial_coords = np.loadtxt(foldpath + '/coords_of_minimum.txt',
    #                             delimiter=',')
    print(initial_coords, "initial_coords")
    # ret = steepest_descent(initial_coords, potential, dx=1e-4)
    # ret = modifiedfire_cpp(initial_coords, potential, iprint=1, tol=1e-4)
    # E, V = print(potential.getEnergyGradient(initial_coords))
    # ret = quench_mixed_optimizer(potential, initial_coords, conv_tol=1e-100)
    # ret = quench_mixed_optimizer(potential, initial_coords, conv_tol=1e-100, nsteps=3000)
    # ret = quench_steepest(
    #     potential,
    #     initial_coords,  # make sure right coords are being passed
    #     stepsize=0.0001,
    #     nsteps=1000,
    #     tol=1e-7)
    # ret = quench_cvode_opt(initial_coords, potential, )
    ret = quench_cvode_opt(initial_coords,
                           potential,
                           tol=1e-9,
                           rtol=1e-10,
                           atol=1e-10)

    finalcoords = ret.coords
    print(ret.coords)
    # np.savetxt(foldpath + '/coords_of_minimum.txt', finalcoords, delimiter=',')
    print(ret)
    # print(ret.coords)
    # print(ret2.coords)
    # print(ret2.coords-ret.coords)
    print(finalcoords)
    E, V, H = potential.getEnergyGradientHessian(finalcoords)
    print(np.linalg.eigvals(H), "all eigenvalues")
Ejemplo n.º 2
0
def setup_inverse_power(SysParams, radii, box_length):
    """ sets up the inverse power potential

    Parameters
    ----------
    SysParams : Enum
        Parameters for the system we wish to study
    radii : np.ndarray
    radii of particles in the system
    box_length : float
    length of the box
    """

    # check that the setup is for a dict
    if type(SysParams) is not dict:
        raise TypeError("system parameters need to be in dictionary form")
    box_vec = np.array([box_length] * int(SysParams["ndim"]))
    print(SysParams["use_cell_lists"])
    potential = InversePower(
        SysParams["power"],
        SysParams["eps"],
        use_cell_lists=SysParams["use_cell_lists"],
        ndim=SysParams["ndim"],
        radii=radii * 1.0,
        boxvec=box_vec,
    )
    return potential
Ejemplo n.º 3
0
    def test_same_minima_InversePower(self):
        nparticles = 32
        hs_radii = 0.05 * np.random.randn(nparticles) + 1
        volpart = np.sum(4. / 3. * np.pi * hs_radii**3)
        phi = 0.7
        boxl = (volpart / phi)**(1 / 3.)
        boxv = [boxl, boxl, boxl]
        coords = np.random.rand(nparticles * 3) * boxl
        ncellx_scale = get_ncellsx_scale(np.ones(nparticles), boxv)
        bdim = 3
        pot_cellists = InversePower(2.5,
                                    1,
                                    hs_radii,
                                    ndim=3,
                                    boxvec=boxv,
                                    use_cell_lists=True,
                                    ncellx_scale=ncellx_scale)
        pot_no_cellists = InversePower(2.5,
                                       1,
                                       hs_radii,
                                       ndim=3,
                                       boxvec=boxv,
                                       use_cell_lists=False,
                                       ncellx_scale=ncellx_scale)

        nsteps = 1000
        tol = 1e-5

        res_cell_lists = lbfgs_cpp(coords,
                                   pot_cellists,
                                   nsteps=nsteps,
                                   tol=tol)
        res_no_cell_lists = lbfgs_cpp(coords,
                                      pot_no_cellists,
                                      nsteps=nsteps,
                                      tol=tol)

        fcoords_cell_lists = res_cell_lists.coords
        fcoords_no_cell_lists = res_no_cell_lists.coords
        # self.assertEqual(fcoords_no_cell_lists,fcoords_cell_lists)
        self.assertTrue(np.all(fcoords_no_cell_lists == fcoords_cell_lists))
Ejemplo n.º 4
0
def quench_single_inverse_power(coord_file_name, foldpath, sub_fold_name,
                                optimizer, opt_param_dict):
    """
    figures out the minimum correspoding to a set of particle coords
    Parameters
    ----------
    coord_file_name: string
        name of the path to the coordinates
    foldername: str
        folder definining the run
    sub_fold_name:
        name of subfolder where the run data is stored
    optimizer: optimizer
        quench
    opt_param_dict: dict
        dictionary of parameters for the optimizer
    """

    sysparams = load_params(foldpath)

    # path to quench coords
    quench_coords_path = (foldpath + "/" + sub_fold_name + "/" + "ensemble/" +
                          coord_file_name)
    quench_coords = np.loadtxt(quench_coords_path)
    radii = get_hs_radii(foldpath, sub_fold_name)
    box_length = get_box_length(radii, sysparams.ndim.value,
                                sysparams.phi.value)

    boxv = [box_length] * sysparams.ndim.value
    ncellx_scale = get_ncellsx_scale(radii, boxv)

    potential = InversePower(
        sysparams.power.value,
        sysparams.eps.value,
        use_cell_lists=False,
        ndim=sysparams.ndim.value,
        radii=radii * 1.0,
        boxvec=boxv,
    )

    boxv = [box_length] * sysparams.ndim.value
    # ncellx_scale = get_ncellsx_scale(radii, boxv)

    print(potential.getEnergy(quench_coords))
    ret = optimizer(quench_coords, potential, **opt_param_dict)
    try:
        ret = optimizer(quench_coords, potential, **opt_param_dict)
    except:
        print("exception occured")
        # if exception occurs, treat as failure. this is for rattlers
        # not enough failures occur that it would make a difference to not just assume this never happens
        # but we shoudl switch this out
        # but jic
        return (quench_coords, False, 0, 0, 0, 0, 0)

    # This exists because some runs don't have hessian evaluations
    try:
        ret["nhev"]
    except:
        ret["nhev"] = 0

    # mixed optimizer statistics
    try:
        ret["n_phase_1"]
    except:
        ret["n_phase_1"] = 0
    # mixed optimizer statistics
    try:
        ret["n_phase_2"]
    except:
        ret["n_phase_2"] = 0

    print(ret.coords - quench_coords)
    print(opt_param_dict)
    results = (
        ret.coords,
        ret.success,
        ret.nfev,
        ret.nsteps,
        ret.nhev,
        ret.n_phase_1,
        ret.n_phase_2,
    )
    print(quench_coords_path)
    return results
Ejemplo n.º 5
0
    0.13492935,
    5.55656566,
    5.19310115,
    5.80610666,
    6.53090015,
    5.3332587,
    3.07972527,
    5.20893375,
]
test_radii = np.array(test_radii)
test_coords = np.array(test_coords)
box_length = 6.673592625078725
potential = InversePower(
    2.5,
    1.0,
    use_cell_lists=False,
    ndim=2.0,
    radii=test_radii,
    boxvec=[box_length, box_length],
)

test_gradient = np.copy(test_coords)

potential.getEnergyGradientInPlace(test_coords, test_gradient)

grad = potential.getEnergyGradient(test_coords)
print(test_gradient)
print(grad)

# np.savetxt('energy.csv', np.array([potential.getEnergy(test_coords)]), delimiter=',')
# np.savetxt('gradient.csv', potential.getEnergyGradient(test_coords)[1], delimiter=',')
# np.savetxt('hessian.csv', potential.getEnergyGradientHessian(test_coords)[2], delimiter=',')
Ejemplo n.º 6
0
 def get_potential(self):
     return InversePower(self.power, self.eps, self.radii, boxvec=self.boxvec, **self.potential_kwargs)
Ejemplo n.º 7
0
    3.16224709493039,
]


ctol = 1e-2
ndim = 2
base_dir = "/home/praharsh/Dropbox/research/bv-libraries/basinerror/datainv"
foldnameInversePower = "ndim=2phi=0.9seed=0n_part=16r1=1.0r2=1.4rstd1=0.05rstd2=0.06999999999999999use_cell_lists=0power=2.5eps=1.0"
data_loc = base_dir + "/" + foldnameInversePower
minima_database_path = data_loc + "minima_database.npy"
(hs_radii, initial_coords, box_length) = load_secondary_params(data_loc)
sysparams = load_params(data_loc)
potential = InversePower(
    sysparams.power.value,
    sysparams.eps.value,
    use_cell_lists=False,
    ndim=sysparams.ndim.value,
    radii=hs_radii * 1.0,
    boxvec=[box_length, box_length],
)
minima_container_debug = CheckSameMinimum(
    ctol,
    ndim,
    boxl=box_length,
    minimalist_max_len=2000,
    minima_database_location=minima_database_path,
    update_database=True,
    rattler_check=True,
    potential=potential,
    hs_radii=hs_radii,
)
Ejemplo n.º 8
0
def compare_runs_2d(fnames, foldpath, subfoldname, run_a, run_b, ctol):
    """ compares runs on points with names fnames between
        runs done with 2 minima find routines run_a and run_b

    Args:
       fnames filenames
       run_a run a 
       run_b run b

    Returns:
        percentage of minima that are the same

    """
    # TODO: replace this
    sysparams = load_params(foldpath)
    radii = get_hs_radii(foldpath, subfoldname)
    box_length = get_box_length(radii, sysparams.ndim.value,
                                sysparams.phi.value)

    subfoldpath = foldpath + "/" + subfoldname
    data_path_a = subfoldpath + "/" + run_a
    data_path_b = subfoldpath + "/" + run_b
    potential = InversePower(
        sysparams.power.value,
        sysparams.eps.value,
        use_cell_lists=False,
        ndim=sysparams.ndim.value,
        radii=radii * 1.0,
        boxvec=[box_length, box_length],
    )
    minima_checker = CheckSameMinimum(ctol,
                                      dim=2,
                                      boxl=box_length,
                                      hs_radii=radii,
                                      potential=potential)

    same_minimum_check_l = []
    for fname in fnames:
        print(fname)
        minimum_a = np.loadtxt(data_path_a + "/" + fname, delimiter=",")
        minimum_b = np.loadtxt(data_path_b + "/" + fname, delimiter=",")
        boxed_minimum_a = minima_checker.box_reshape_coords(minimum_a)
        boxed_minimum_b = minima_checker.box_reshape_coords(minimum_b)
        # get first index that is not a rattler
        rattlers_exist, rattlers = minima_checker._find_rattlers(minimum_a)
        print(rattlers)
        # number of non rattlers to ensure we're not in a fluid state
        n_non_rattlers = np.count_nonzero(rattlers)
        # only do calculations if all particles aren't rattlers
        if n_non_rattlers != 0:
            first_non_rattler = (np.argwhere(rattlers != 0).T)[0, 0]
            # TODO: rewrite the CheckSameMinimum function
            # load and make sure the particle being aligned is not a rattler later
            # we're choosing -1 because that's always going to be of radius 1.4
            #  particle
            aligned_minimum_b = minima_checker.align_structures(
                boxed_minimum_a, boxed_minimum_b, part_ind=first_non_rattler)
            same_minimum_check = minima_checker.check_same_structure(
                aligned_minimum_b, boxed_minimum_a, rattlers)
            same_minimum_check_l.append(same_minimum_check)

    fraction_same_minimum = np.mean(same_minimum_check_l)
    print(fraction_same_minimum)
    return fraction_same_minimum
def map_binary_inversepower(
    foldername,
    particle_coords,
    optimizer,
    parameter_dict,
    random_coord_0=0,
    random_coord_1=-1,
    z=0,
):
    """
    Finds whether a point defined by particle_coord
    on the meshgrid correspond to a minimum or not for a 2d
    case.
    """
    foldpath = BASE_DIRECTORY + "/" + foldername
    # import params
    sysparams = load_params(foldpath)
    (hs_radii, initial_coords, box_length) = load_secondary_params(foldpath)
    assert sysparams.ndim.value == 2
    minimum_coords = np.loadtxt(foldpath + "/coords_of_minimum.txt", delimiter=",")
    quench_coords = initial_coords.copy()

    if len(quench_coords) == 16:
        quench_coords = (
            quench_coords
            + particle_coords[0] * VEC_8_0
            + particle_coords[1] * VEC_8_1
            + z * VEC_8_2
        )
    elif len(quench_coords) == 32:
        quench_coords = (
            quench_coords
            + particle_coords[0] * VEC_16_0
            + particle_coords[1] * VEC_16_1
            + z * VEC_16_2
        )
    elif len(quench_coords) == 64:
        quench_coords = (
            quench_coords
            + particle_coords[0] * VEC_16_0
            + particle_coords[1] * VEC_16_1
            + z * VEC_16_2
        )
    else:
        raise Exception("error other random coords have not been generated")

    print(quench_coords, "quench")
    # print(quench_coords, 'quench coords')
    # box length
    box_length = float(box_length)
    boxv = [box_length] * sysparams.ndim.value
    ncellx_scale = get_ncellsx_scale(hs_radii, boxv)
    potential = InversePower(
        sysparams.power.value,
        sysparams.eps.value,
        use_cell_lists=False,
        ndim=sysparams.ndim.value,
        radii=hs_radii * 1.0,
        boxvec=boxv,
    )

    # ret = quench_mixed_optimizer(potential,
    #                              quench_coords,  # make sure right coords are being passed
    #                              T=10,
    #                              step=1,
    #                              nsteps=100000,
    #                              conv_tol=1e-8,
    #                              tol=1e-6, rtol=1e-4, atol=1e-4)
    # ret = quench_steepest(
    #     potential,
    #     quench_coords,  # make sure right coords are being passed
    #     nsteps=2000000,
    #     stepsize=5e-3,  # for steepest descent step size should be small
    #     tol=1e-4)
    # ret = quench_cvode_opt(potential, quench_coords, tol=1e-6, rtol=1e-4, atol=1e-4)
    try:
        ret = optimizer(quench_coords, potential, **parameter_dict)
    except:
        print(quench_coords, "failed here")
        print(initial_coords, "coords")
        print(len(quench_coords))
        raise Exception("failure")

    # ret = lbfgs_cpp(quench_coords, potential, tol=1e-8, M=1)

    # This exists because some runs don't have hessian evaluations
    try:
        ret["nhev"]
    except:
        ret["nhev"] = 0
    coordarg = 0
    results = (ret.coords, ret.success, coordarg, ret.nfev, ret.nsteps, ret.nhev)
    # the reason the potential is being passed is because quench coords needs the potential to figure out what to do
    return results
def map_pointset_loop_xy(
    foldname,
    pointset,
    optimizer,
    parameter_dict,
    ctol=1e-2,
    ndim=2,
    use_minima_database=True,
    minima_database_path=None,
    coord_arg_0=0,
    coord_arg_1=1,
    z=0,
):
    """ Checks a bunch of points if they match to a minimum by using a for loop
    """
    is_same_minimum_list = []
    resultlist = []
    foldpath = BASE_DIRECTORY + "/" + foldname

    sysparams = load_params(foldpath)
    (hs_radii, initial_coords, box_length) = load_secondary_params(foldpath)
    minimum_coords = np.loadtxt(foldpath + "/coords_of_minimum.txt", delimiter=",")

    # Initialize CheckSameMinimum
    potential = InversePower(
        sysparams.power.value,
        sysparams.eps.value,
        use_cell_lists=False,
        ndim=sysparams.ndim.value,
        radii=hs_radii * 1.0,
        boxvec=[box_length, box_length],
    )
    minima_container = CheckSameMinimum(
        ctol,
        ndim,
        boxl=box_length,
        minimalist_max_len=200000,
        minima_database_location=minima_database_path,
        update_database=True,
        rattler_check=True,
        potential=potential,
        hs_radii=hs_radii,
    )

    if use_minima_database == True:
        try:
            minima_container.minimalist = [
                minima_container.box_reshape_coords(x)
                for x in np.load(minima_database_path)
            ]
        except:
            print("warning no minima data found. generating")
            minima_container.minimalist = [
                # minima_container.box_reshape_coords(minimum_coords)
            ]
    nfevlist = []
    nstepslist = []
    nhevlist = []
    for index, point in enumerate(pointset):
        res = map_binary_inversepower(
            foldname,
            point,
            optimizer,
            parameter_dict,
            random_coord_0=coord_arg_0,
            random_coord_1=coord_arg_1,
            z=z,
        )
        minima_container.add_minimum(res[0], point, res[2])
        # print(index)
        # print(minima_container.nrattlermin, 'nrattlermin')
        # print(minima_container.nfluidstates, 'nfluidstates')
        nfevlist.append(res[3])
        nstepslist.append(res[4])
        nhevlist.append(res[5])

    # print(np.average(nfevlist), 'number of function evaluations')
    # print(np.average(nstepslist), 'number of steps')
    # print(np.average(nstepslist), 'number of steps')
    # print(np.average(nhevlist), "number of hessian evaluations")

    # print(minima_container.orderparamlist)

    foldpathdata = foldpath + "/" + QUENCH_FOLDER_NAME + "/z_data_30_l6/" + str(z)
    os.makedirs(foldpathdata, exist_ok=True)
    minima_container.dump_map(foldpathdata)

    run_diagnostics = {}
    run_diagnostics["nfev"] = float(np.average(nfevlist))
    run_diagnostics["nhev"] = float(np.average(nhevlist))
    run_diagnostics["nsteps"] = float(np.average(nstepslist))

    # print(minima_container.initial_coords_list)
    # print(minima_container.orderparamlist)
    # print(minima_container.orderparamlist)
    return run_diagnostics, is_same_minimum_list, resultlist
Ejemplo n.º 11
0
def map_binary_inversepower(
    foldername,
    particle_coords,
    optimizer,
    parameter_dict,
    random_coord_0=0,
    random_coord_1=-1,
    z=0,
    index=None,
    mesh_length=None,
):
    """
    Finds whether a point defined by particle_coord
    on the meshgrid correspond to a minimum or not for a 2d
    case.
    """
    foldpath = BASE_DIRECTORY + "/" + foldername
    # import params
    sysparams = load_params(foldpath)
    (hs_radii, initial_coords, box_length) = load_secondary_params(foldpath)
    assert sysparams.ndim.value == 2
    quench_coords = initial_coords.copy()
    print("initial_coords", initial_coords)

    print(particle_coords[0], particle_coords[1],
          "vector coefficients as passed")
    print("z value")
    quench_coords = (quench_coords + particle_coords[0] * VEC_16_0 +
                     particle_coords[1] * VEC_16_1 + z * VEC_16_2)

    # TODO: save this as a unit meshgrid
    # print(quench_coords, 'quench coords')
    # box length
    box_length = float(box_length)
    boxv = [box_length] * sysparams.ndim.value
    ncellx_scale = get_ncellsx_scale(hs_radii, boxv)
    print(hs_radii)
    potential = InversePower(
        sysparams.power.value,
        sysparams.eps.value,
        use_cell_lists=False,
        ndim=sysparams.ndim.value,
        radii=hs_radii * 1.0,
        boxvec=boxv,
    )
    # save potential parameters
    # writing this out since Enum to dict conversion does not give the mapping we want
    potential_params_fname = "potential_params.yaml"
    potential_param_dict = {
        "ndim": sysparams.ndim.value,
        "phi": sysparams.phi.value,
        "seed": 0,
        "n_part": sysparams.n_part.value,
        "r1": sysparams.r1.value,
        "r2": sysparams.r2.value,
        "rstd1": sysparams.rstd1.value,
        "rstd2": sysparams.rstd2.value,
        "use_cell_lists": int(False),
        "power": sysparams.power.value,
        "eps": sysparams.eps.value,
    }

    param_str = generate_param_str_from_dict(potential_param_dict)

    # make directory to emulate structure by refactored code
    emulation_folder = COMPARE_FOLDER_NAME + "/" + param_str
    # add README to emulation folder
    os.makedirs(emulation_folder, exist_ok=True)
    with open(COMPARE_FOLDER_NAME + "/README.org", "w") as f:
        f.write(" \#+AUTHOR Praharsh Suryadevara .\n")
        f.write("* Read First.")
        f.write(
            "This dirctory has been auto-generated by old code before refactoring. Do not change.\n"
        )

    # emulates path of the refactored code
    ensemble_folder_path = emulation_folder + "/ensemble/random_plane"
    os.makedirs(ensemble_folder_path, exist_ok=True)

    # 0 to emulate the addition of seed
    sec_param_folder_path = emulation_folder + "/sec_params/0"
    os.makedirs(sec_param_folder_path, exist_ok=True)

    old_code_results_folder_path = emulation_folder + "/old_data"
    os.makedirs(old_code_results_folder_path, exist_ok=True)

    with open(emulation_folder + "/params.yaml", "w") as param_file:
        yaml.dump(potential_param_dict, param_file)

    opt_param_fname = "minima_finder_params.yaml"
    with open(emulation_folder + "/" + opt_param_fname, "w") as param_file:
        yaml.dump(parameter_dict, param_file)

    initial_coords_fname = str(index) + ".txt"
    np.savetxt(ensemble_folder_path + "/" + initial_coords_fname,
               quench_coords,
               delimiter=",")

    mesh_coords_fname = str(index) + "_mesh.txt"
    np.savetxt(ensemble_folder_path + "/" + mesh_coords_fname,
               particle_coords,
               delimiter=",")

    if mesh_length is not None:
        # 2 *[particle_coords[0], particle_coords[1]]/mesh_length -0.5 form our unit mesh_grid
        # centered around [0, 0]
        mesh_coords = (2 * np.array([particle_coords[0], particle_coords[1]]) /
                       mesh_length - 0.5)
        mesh_coords_fname = str(index) + "_mesh.txt"
        np.savetxt(ensemble_folder_path + "/" + mesh_coords_fname,
                   mesh_coords,
                   delimiter=",")

    np.savetxt(
        sec_param_folder_path + "/" + "initial_coords.txt",
        initial_coords,
        delimiter=",",
    )
    np.savetxt(
        sec_param_folder_path + "/" + "box_length.txt",
        np.array([box_length]),
        delimiter=",",
    )
    np.savetxt(sec_param_folder_path + "/" + "hs_radii.txt",
               hs_radii,
               delimiter=",")

    # ret = quench_mixed_optimizer(potential,
    #                              quench_coords,  # make sure right coords are being passed
    #                              T=10,
    #                              step=1,
    #                              nsteps=100000,
    #                              conv_tol=1e-8,
    #                              tol=1e-6, rtol=1e-4, atol=1e-4)
    # ret = quench_steepest(
    #     potential,
    #     quench_coords,  # make sure right coords are being passed
    #     nsteps=2000000,
    #     stepsize=5e-3,  # for steepest descent step size should be small
    #     tol=1e-4)
    # ret = quench_cvode_opt(potential, quench_coords, tol=1e-6, rtol=1e-4, atol=1e-4)
    try:
        ret = optimizer(quench_coords, potential, **parameter_dict)
    except:
        raise Exception("failure")
    # ret = lbfgs_cpp(quench_coords, potential, tol=1e-8, M=1)

    # This exists because some runs don't have hessian evaluations
    try:
        ret["nhev"]
    except:
        ret["nhev"] = 0
    coordarg = 0

    final_coords = ret["coords"]
    final_coords_fname = str(index) + "_coords.txt"
    np.savetxt(
        old_code_results_folder_path + "/" + final_coords_fname,
        final_coords,
        delimiter=",",
    )

    energy, grad, hess = potential.getEnergyGradientHessian(final_coords)

    hessian_fname = str(index) + "_hessian.txt"
    np.savetxt(old_code_results_folder_path + "/" + hessian_fname,
               hess,
               delimiter=",")
    grad_fname = str(index) + "_grad.txt"
    np.savetxt(old_code_results_folder_path + "/" + grad_fname,
               grad,
               delimiter=",")

    last_step_fname = str(index) + "_step.txt"
    # np.savetxt(old_code_results_folder_path + '/' +
    #            last_step_fname, ret['step'], delimiter=',')

    # check that the minimum hessian eigenvalue is positive
    # print out the eigenvector corresponding to it
    eigvals, eigvecs = np.linalg.eigh(hess)
    print(eigvals[1])
    print(eigvecs[:, 1])

    # step_in_eigvec_basis = np.matmul(eigvecs.T, ret['step'])
    # step_in_eigvec_basis_normalized = step_in_eigvec_basis/np.linalg.norm(step_in_eigvec_basis)

    # -1e-15 is to avoid numerical issues
    if np.min(eigvals) < -1e-3:
        print("minimum Eigenvalue: ", np.min(eigvals))
        print("Eigenvalues: ", eigvals)
        raise Exception("negative eigenvalue")

    # save heuristics as a dict
    results = (
        ret.coords,
        ret.success,
        coordarg,
        ret.nfev,
        ret.nsteps,
        ret.nhev,
        eigvecs,
    )

    # simplified dictionary since we're only using the success variable
    res_dict = {
        "success": bool(ret.success),
        "nsteps": ret.nsteps,
        "energy": ret.energy,
        "nfev": ret.nfev,
    }
    # print("-------------------------------------------------steppp", step_in_eigvec_basis_normalized)
    # print("eigenvalues", eigvals)

    yaml_fname = str(index) + ".yaml"
    # save heuristics as yaml
    with open(old_code_results_folder_path + "/" + yaml_fname, "w") as f:
        yaml.dump(res_dict, f)

    print(ret.nsteps, "nsteps")
    # the reason the potential is being passed is because quench coords needs the potential to figure out what to do
    return results