mlmm-toolkit 0.2.2.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hessian_ff/__init__.py +50 -0
- hessian_ff/analytical_hessian.py +609 -0
- hessian_ff/constants.py +46 -0
- hessian_ff/forcefield.py +339 -0
- hessian_ff/loaders.py +608 -0
- hessian_ff/native/Makefile +8 -0
- hessian_ff/native/__init__.py +28 -0
- hessian_ff/native/analytical_hessian.py +88 -0
- hessian_ff/native/analytical_hessian_ext.cpp +258 -0
- hessian_ff/native/bonded.py +82 -0
- hessian_ff/native/bonded_ext.cpp +640 -0
- hessian_ff/native/loader.py +349 -0
- hessian_ff/native/nonbonded.py +118 -0
- hessian_ff/native/nonbonded_ext.cpp +1150 -0
- hessian_ff/prmtop_parmed.py +23 -0
- hessian_ff/system.py +107 -0
- hessian_ff/terms/__init__.py +14 -0
- hessian_ff/terms/angle.py +73 -0
- hessian_ff/terms/bond.py +44 -0
- hessian_ff/terms/cmap.py +406 -0
- hessian_ff/terms/dihedral.py +141 -0
- hessian_ff/terms/nonbonded.py +209 -0
- hessian_ff/tests/__init__.py +0 -0
- hessian_ff/tests/conftest.py +75 -0
- hessian_ff/tests/data/small/complex.parm7 +1346 -0
- hessian_ff/tests/data/small/complex.pdb +125 -0
- hessian_ff/tests/data/small/complex.rst7 +63 -0
- hessian_ff/tests/test_coords_input.py +44 -0
- hessian_ff/tests/test_energy_force.py +49 -0
- hessian_ff/tests/test_hessian.py +137 -0
- hessian_ff/tests/test_smoke.py +18 -0
- hessian_ff/tests/test_validation.py +40 -0
- hessian_ff/workflows.py +889 -0
- mlmm/__init__.py +36 -0
- mlmm/__main__.py +7 -0
- mlmm/_version.py +34 -0
- mlmm/add_elem_info.py +374 -0
- mlmm/advanced_help.py +91 -0
- mlmm/align_freeze_atoms.py +601 -0
- mlmm/all.py +3535 -0
- mlmm/bond_changes.py +231 -0
- mlmm/bool_compat.py +223 -0
- mlmm/cli.py +574 -0
- mlmm/cli_utils.py +166 -0
- mlmm/default_group.py +337 -0
- mlmm/defaults.py +467 -0
- mlmm/define_layer.py +526 -0
- mlmm/dft.py +1041 -0
- mlmm/energy_diagram.py +253 -0
- mlmm/extract.py +2213 -0
- mlmm/fix_altloc.py +464 -0
- mlmm/freq.py +1406 -0
- mlmm/harmonic_constraints.py +140 -0
- mlmm/hessian_cache.py +44 -0
- mlmm/hessian_calc.py +174 -0
- mlmm/irc.py +638 -0
- mlmm/mlmm_calc.py +2262 -0
- mlmm/mm_parm.py +945 -0
- mlmm/oniom_export.py +1983 -0
- mlmm/oniom_import.py +457 -0
- mlmm/opt.py +1742 -0
- mlmm/path_opt.py +1353 -0
- mlmm/path_search.py +2299 -0
- mlmm/preflight.py +88 -0
- mlmm/py.typed +1 -0
- mlmm/pysis_runner.py +45 -0
- mlmm/scan.py +1047 -0
- mlmm/scan2d.py +1226 -0
- mlmm/scan3d.py +1265 -0
- mlmm/scan_common.py +184 -0
- mlmm/summary_log.py +736 -0
- mlmm/trj2fig.py +448 -0
- mlmm/tsopt.py +2871 -0
- mlmm/utils.py +2309 -0
- mlmm/xtb_embedcharge_correction.py +475 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/METADATA +1159 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/RECORD +372 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/WHEEL +5 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/entry_points.txt +2 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/licenses/LICENSE +674 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/top_level.txt +4 -0
- pysisyphus/Geometry.py +1667 -0
- pysisyphus/LICENSE +674 -0
- pysisyphus/TableFormatter.py +63 -0
- pysisyphus/TablePrinter.py +74 -0
- pysisyphus/__init__.py +12 -0
- pysisyphus/calculators/AFIR.py +452 -0
- pysisyphus/calculators/AnaPot.py +20 -0
- pysisyphus/calculators/AnaPot2.py +48 -0
- pysisyphus/calculators/AnaPot3.py +12 -0
- pysisyphus/calculators/AnaPot4.py +20 -0
- pysisyphus/calculators/AnaPotBase.py +337 -0
- pysisyphus/calculators/AnaPotCBM.py +25 -0
- pysisyphus/calculators/AtomAtomTransTorque.py +154 -0
- pysisyphus/calculators/CFOUR.py +250 -0
- pysisyphus/calculators/Calculator.py +844 -0
- pysisyphus/calculators/CerjanMiller.py +24 -0
- pysisyphus/calculators/Composite.py +123 -0
- pysisyphus/calculators/ConicalIntersection.py +171 -0
- pysisyphus/calculators/DFTBp.py +430 -0
- pysisyphus/calculators/DFTD3.py +66 -0
- pysisyphus/calculators/DFTD4.py +84 -0
- pysisyphus/calculators/Dalton.py +61 -0
- pysisyphus/calculators/Dimer.py +681 -0
- pysisyphus/calculators/Dummy.py +20 -0
- pysisyphus/calculators/EGO.py +76 -0
- pysisyphus/calculators/EnergyMin.py +224 -0
- pysisyphus/calculators/ExternalPotential.py +264 -0
- pysisyphus/calculators/FakeASE.py +35 -0
- pysisyphus/calculators/FourWellAnaPot.py +28 -0
- pysisyphus/calculators/FreeEndNEBPot.py +39 -0
- pysisyphus/calculators/Gaussian09.py +18 -0
- pysisyphus/calculators/Gaussian16.py +726 -0
- pysisyphus/calculators/HardSphere.py +159 -0
- pysisyphus/calculators/IDPPCalculator.py +49 -0
- pysisyphus/calculators/IPIClient.py +133 -0
- pysisyphus/calculators/IPIServer.py +234 -0
- pysisyphus/calculators/LEPSBase.py +24 -0
- pysisyphus/calculators/LEPSExpr.py +139 -0
- pysisyphus/calculators/LennardJones.py +80 -0
- pysisyphus/calculators/MOPAC.py +219 -0
- pysisyphus/calculators/MullerBrownSympyPot.py +51 -0
- pysisyphus/calculators/MultiCalc.py +85 -0
- pysisyphus/calculators/NFK.py +45 -0
- pysisyphus/calculators/OBabel.py +87 -0
- pysisyphus/calculators/ONIOMv2.py +1129 -0
- pysisyphus/calculators/ORCA.py +893 -0
- pysisyphus/calculators/ORCA5.py +6 -0
- pysisyphus/calculators/OpenMM.py +88 -0
- pysisyphus/calculators/OpenMolcas.py +281 -0
- pysisyphus/calculators/OverlapCalculator.py +908 -0
- pysisyphus/calculators/Psi4.py +218 -0
- pysisyphus/calculators/PyPsi4.py +37 -0
- pysisyphus/calculators/PySCF.py +341 -0
- pysisyphus/calculators/PyXTB.py +73 -0
- pysisyphus/calculators/QCEngine.py +106 -0
- pysisyphus/calculators/Rastrigin.py +22 -0
- pysisyphus/calculators/Remote.py +76 -0
- pysisyphus/calculators/Rosenbrock.py +15 -0
- pysisyphus/calculators/SocketCalc.py +97 -0
- pysisyphus/calculators/TIP3P.py +111 -0
- pysisyphus/calculators/TransTorque.py +161 -0
- pysisyphus/calculators/Turbomole.py +965 -0
- pysisyphus/calculators/VRIPot.py +37 -0
- pysisyphus/calculators/WFOWrapper.py +333 -0
- pysisyphus/calculators/WFOWrapper2.py +341 -0
- pysisyphus/calculators/XTB.py +418 -0
- pysisyphus/calculators/__init__.py +81 -0
- pysisyphus/calculators/cosmo_data.py +139 -0
- pysisyphus/calculators/parser.py +150 -0
- pysisyphus/color.py +19 -0
- pysisyphus/config.py +133 -0
- pysisyphus/constants.py +65 -0
- pysisyphus/cos/AdaptiveNEB.py +230 -0
- pysisyphus/cos/ChainOfStates.py +725 -0
- pysisyphus/cos/FreeEndNEB.py +25 -0
- pysisyphus/cos/FreezingString.py +103 -0
- pysisyphus/cos/GrowingChainOfStates.py +71 -0
- pysisyphus/cos/GrowingNT.py +309 -0
- pysisyphus/cos/GrowingString.py +508 -0
- pysisyphus/cos/NEB.py +189 -0
- pysisyphus/cos/SimpleZTS.py +64 -0
- pysisyphus/cos/__init__.py +22 -0
- pysisyphus/cos/stiffness.py +199 -0
- pysisyphus/drivers/__init__.py +17 -0
- pysisyphus/drivers/afir.py +855 -0
- pysisyphus/drivers/barriers.py +271 -0
- pysisyphus/drivers/birkholz.py +138 -0
- pysisyphus/drivers/cluster.py +318 -0
- pysisyphus/drivers/diabatization.py +133 -0
- pysisyphus/drivers/merge.py +368 -0
- pysisyphus/drivers/merge_mol2.py +322 -0
- pysisyphus/drivers/opt.py +375 -0
- pysisyphus/drivers/perf.py +91 -0
- pysisyphus/drivers/pka.py +52 -0
- pysisyphus/drivers/precon_pos_rot.py +669 -0
- pysisyphus/drivers/rates.py +480 -0
- pysisyphus/drivers/replace.py +219 -0
- pysisyphus/drivers/scan.py +212 -0
- pysisyphus/drivers/spectrum.py +166 -0
- pysisyphus/drivers/thermo.py +31 -0
- pysisyphus/dynamics/Gaussian.py +103 -0
- pysisyphus/dynamics/__init__.py +20 -0
- pysisyphus/dynamics/colvars.py +136 -0
- pysisyphus/dynamics/driver.py +297 -0
- pysisyphus/dynamics/helpers.py +256 -0
- pysisyphus/dynamics/lincs.py +105 -0
- pysisyphus/dynamics/mdp.py +364 -0
- pysisyphus/dynamics/rattle.py +121 -0
- pysisyphus/dynamics/thermostats.py +128 -0
- pysisyphus/dynamics/wigner.py +266 -0
- pysisyphus/elem_data.py +3473 -0
- pysisyphus/exceptions.py +2 -0
- pysisyphus/filtertrj.py +69 -0
- pysisyphus/helpers.py +623 -0
- pysisyphus/helpers_pure.py +649 -0
- pysisyphus/init_logging.py +50 -0
- pysisyphus/intcoords/Bend.py +69 -0
- pysisyphus/intcoords/Bend2.py +25 -0
- pysisyphus/intcoords/BondedFragment.py +32 -0
- pysisyphus/intcoords/Cartesian.py +41 -0
- pysisyphus/intcoords/CartesianCoords.py +140 -0
- pysisyphus/intcoords/Coords.py +56 -0
- pysisyphus/intcoords/DLC.py +197 -0
- pysisyphus/intcoords/DistanceFunction.py +34 -0
- pysisyphus/intcoords/DummyImproper.py +70 -0
- pysisyphus/intcoords/DummyTorsion.py +72 -0
- pysisyphus/intcoords/LinearBend.py +105 -0
- pysisyphus/intcoords/LinearDisplacement.py +80 -0
- pysisyphus/intcoords/OutOfPlane.py +59 -0
- pysisyphus/intcoords/PrimTypes.py +286 -0
- pysisyphus/intcoords/Primitive.py +137 -0
- pysisyphus/intcoords/RedundantCoords.py +659 -0
- pysisyphus/intcoords/RobustTorsion.py +59 -0
- pysisyphus/intcoords/Rotation.py +147 -0
- pysisyphus/intcoords/Stretch.py +31 -0
- pysisyphus/intcoords/Torsion.py +101 -0
- pysisyphus/intcoords/Torsion2.py +25 -0
- pysisyphus/intcoords/Translation.py +45 -0
- pysisyphus/intcoords/__init__.py +61 -0
- pysisyphus/intcoords/augment_bonds.py +126 -0
- pysisyphus/intcoords/derivatives.py +10512 -0
- pysisyphus/intcoords/eval.py +80 -0
- pysisyphus/intcoords/exceptions.py +37 -0
- pysisyphus/intcoords/findiffs.py +48 -0
- pysisyphus/intcoords/generate_derivatives.py +414 -0
- pysisyphus/intcoords/helpers.py +235 -0
- pysisyphus/intcoords/logging_conf.py +10 -0
- pysisyphus/intcoords/mp_derivatives.py +10836 -0
- pysisyphus/intcoords/setup.py +962 -0
- pysisyphus/intcoords/setup_fast.py +176 -0
- pysisyphus/intcoords/update.py +272 -0
- pysisyphus/intcoords/valid.py +89 -0
- pysisyphus/interpolate/Geodesic.py +93 -0
- pysisyphus/interpolate/IDPP.py +55 -0
- pysisyphus/interpolate/Interpolator.py +116 -0
- pysisyphus/interpolate/LST.py +70 -0
- pysisyphus/interpolate/Redund.py +152 -0
- pysisyphus/interpolate/__init__.py +9 -0
- pysisyphus/interpolate/helpers.py +34 -0
- pysisyphus/io/__init__.py +22 -0
- pysisyphus/io/aomix.py +178 -0
- pysisyphus/io/cjson.py +24 -0
- pysisyphus/io/crd.py +101 -0
- pysisyphus/io/cube.py +220 -0
- pysisyphus/io/fchk.py +184 -0
- pysisyphus/io/hdf5.py +49 -0
- pysisyphus/io/hessian.py +72 -0
- pysisyphus/io/mol2.py +146 -0
- pysisyphus/io/molden.py +293 -0
- pysisyphus/io/orca.py +189 -0
- pysisyphus/io/pdb.py +269 -0
- pysisyphus/io/psf.py +79 -0
- pysisyphus/io/pubchem.py +31 -0
- pysisyphus/io/qcschema.py +34 -0
- pysisyphus/io/sdf.py +29 -0
- pysisyphus/io/xyz.py +61 -0
- pysisyphus/io/zmat.py +175 -0
- pysisyphus/irc/DWI.py +108 -0
- pysisyphus/irc/DampedVelocityVerlet.py +134 -0
- pysisyphus/irc/Euler.py +22 -0
- pysisyphus/irc/EulerPC.py +345 -0
- pysisyphus/irc/GonzalezSchlegel.py +187 -0
- pysisyphus/irc/IMKMod.py +164 -0
- pysisyphus/irc/IRC.py +878 -0
- pysisyphus/irc/IRCDummy.py +10 -0
- pysisyphus/irc/Instanton.py +307 -0
- pysisyphus/irc/LQA.py +53 -0
- pysisyphus/irc/ModeKill.py +136 -0
- pysisyphus/irc/ParamPlot.py +53 -0
- pysisyphus/irc/RK4.py +36 -0
- pysisyphus/irc/__init__.py +31 -0
- pysisyphus/irc/initial_displ.py +219 -0
- pysisyphus/linalg.py +411 -0
- pysisyphus/line_searches/Backtracking.py +88 -0
- pysisyphus/line_searches/HagerZhang.py +184 -0
- pysisyphus/line_searches/LineSearch.py +232 -0
- pysisyphus/line_searches/StrongWolfe.py +108 -0
- pysisyphus/line_searches/__init__.py +9 -0
- pysisyphus/line_searches/interpol.py +15 -0
- pysisyphus/modefollow/NormalMode.py +40 -0
- pysisyphus/modefollow/__init__.py +10 -0
- pysisyphus/modefollow/davidson.py +199 -0
- pysisyphus/modefollow/lanczos.py +95 -0
- pysisyphus/optimizers/BFGS.py +99 -0
- pysisyphus/optimizers/BacktrackingOptimizer.py +113 -0
- pysisyphus/optimizers/ConjugateGradient.py +98 -0
- pysisyphus/optimizers/CubicNewton.py +75 -0
- pysisyphus/optimizers/FIRE.py +113 -0
- pysisyphus/optimizers/HessianOptimizer.py +1176 -0
- pysisyphus/optimizers/LBFGS.py +228 -0
- pysisyphus/optimizers/LayerOpt.py +411 -0
- pysisyphus/optimizers/MicroOptimizer.py +169 -0
- pysisyphus/optimizers/NCOptimizer.py +90 -0
- pysisyphus/optimizers/Optimizer.py +1084 -0
- pysisyphus/optimizers/PreconLBFGS.py +260 -0
- pysisyphus/optimizers/PreconSteepestDescent.py +7 -0
- pysisyphus/optimizers/QuickMin.py +74 -0
- pysisyphus/optimizers/RFOptimizer.py +181 -0
- pysisyphus/optimizers/RSA.py +99 -0
- pysisyphus/optimizers/StabilizedQNMethod.py +248 -0
- pysisyphus/optimizers/SteepestDescent.py +23 -0
- pysisyphus/optimizers/StringOptimizer.py +173 -0
- pysisyphus/optimizers/__init__.py +41 -0
- pysisyphus/optimizers/closures.py +301 -0
- pysisyphus/optimizers/cls_map.py +58 -0
- pysisyphus/optimizers/exceptions.py +6 -0
- pysisyphus/optimizers/gdiis.py +280 -0
- pysisyphus/optimizers/guess_hessians.py +311 -0
- pysisyphus/optimizers/hessian_updates.py +355 -0
- pysisyphus/optimizers/poly_fit.py +285 -0
- pysisyphus/optimizers/precon.py +153 -0
- pysisyphus/optimizers/restrict_step.py +24 -0
- pysisyphus/pack.py +172 -0
- pysisyphus/peakdetect.py +948 -0
- pysisyphus/plot.py +1031 -0
- pysisyphus/run.py +2106 -0
- pysisyphus/socket_helper.py +74 -0
- pysisyphus/stocastic/FragmentKick.py +132 -0
- pysisyphus/stocastic/Kick.py +81 -0
- pysisyphus/stocastic/Pipeline.py +303 -0
- pysisyphus/stocastic/__init__.py +21 -0
- pysisyphus/stocastic/align.py +127 -0
- pysisyphus/testing.py +96 -0
- pysisyphus/thermo.py +156 -0
- pysisyphus/trj.py +824 -0
- pysisyphus/tsoptimizers/RSIRFOptimizer.py +56 -0
- pysisyphus/tsoptimizers/RSPRFOptimizer.py +182 -0
- pysisyphus/tsoptimizers/TRIM.py +59 -0
- pysisyphus/tsoptimizers/TSHessianOptimizer.py +463 -0
- pysisyphus/tsoptimizers/__init__.py +23 -0
- pysisyphus/wavefunction/Basis.py +239 -0
- pysisyphus/wavefunction/DIIS.py +76 -0
- pysisyphus/wavefunction/__init__.py +25 -0
- pysisyphus/wavefunction/build_ext.py +42 -0
- pysisyphus/wavefunction/cart2sph.py +190 -0
- pysisyphus/wavefunction/diabatization.py +304 -0
- pysisyphus/wavefunction/excited_states.py +435 -0
- pysisyphus/wavefunction/gen_ints.py +1811 -0
- pysisyphus/wavefunction/helpers.py +104 -0
- pysisyphus/wavefunction/ints/__init__.py +0 -0
- pysisyphus/wavefunction/ints/boys.py +193 -0
- pysisyphus/wavefunction/ints/boys_table_N_64_xasym_27.1_step_0.01.npy +0 -0
- pysisyphus/wavefunction/ints/cart_gto3d.py +176 -0
- pysisyphus/wavefunction/ints/coulomb3d.py +25928 -0
- pysisyphus/wavefunction/ints/diag_quadrupole3d.py +10036 -0
- pysisyphus/wavefunction/ints/dipole3d.py +8762 -0
- pysisyphus/wavefunction/ints/int2c2e3d.py +7198 -0
- pysisyphus/wavefunction/ints/int3c2e3d_sph.py +65040 -0
- pysisyphus/wavefunction/ints/kinetic3d.py +8240 -0
- pysisyphus/wavefunction/ints/ovlp3d.py +3777 -0
- pysisyphus/wavefunction/ints/quadrupole3d.py +15054 -0
- pysisyphus/wavefunction/ints/self_ovlp3d.py +198 -0
- pysisyphus/wavefunction/localization.py +458 -0
- pysisyphus/wavefunction/multipole.py +159 -0
- pysisyphus/wavefunction/normalization.py +36 -0
- pysisyphus/wavefunction/pop_analysis.py +134 -0
- pysisyphus/wavefunction/shells.py +1171 -0
- pysisyphus/wavefunction/wavefunction.py +504 -0
- pysisyphus/wrapper/__init__.py +11 -0
- pysisyphus/wrapper/exceptions.py +2 -0
- pysisyphus/wrapper/jmol.py +120 -0
- pysisyphus/wrapper/mwfn.py +169 -0
- pysisyphus/wrapper/packmol.py +71 -0
- pysisyphus/xyzloader.py +168 -0
- pysisyphus/yaml_mods.py +45 -0
- thermoanalysis/LICENSE +674 -0
- thermoanalysis/QCData.py +244 -0
- thermoanalysis/__init__.py +0 -0
- thermoanalysis/config.py +3 -0
- thermoanalysis/constants.py +20 -0
- thermoanalysis/thermo.py +1011 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pysisyphus.line_searches.LineSearch import LineSearch, \
|
|
4
|
+
LineSearchNotConverged
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HagerZhang(LineSearch):
|
|
8
|
+
|
|
9
|
+
def __init__(self, *args, alpha_prev=None, f_prev=None, dphi0_prev=None,
|
|
10
|
+
quad_step=False, eps=1e-6, theta=0.5, gamma=0.5, rho=5,
|
|
11
|
+
psi_0=.01, psi_1=.1, psi_2=2., psi_low=0.1, psi_hi=10,
|
|
12
|
+
Delta=.7, omega=1e-3, max_bisects=10, **kwargs):
|
|
13
|
+
|
|
14
|
+
kwargs["cond"] = "wolfe"
|
|
15
|
+
super().__init__(*args, **kwargs)
|
|
16
|
+
|
|
17
|
+
self.alpha_prev = alpha_prev
|
|
18
|
+
self.f_prev = f_prev
|
|
19
|
+
self.dphi0_prev = dphi0_prev
|
|
20
|
+
self.quad_step = quad_step
|
|
21
|
+
|
|
22
|
+
self.eps = eps
|
|
23
|
+
self.theta = theta
|
|
24
|
+
self.gamma = gamma
|
|
25
|
+
self.rho = rho
|
|
26
|
+
self.psi_0 = psi_0
|
|
27
|
+
self.psi_1 = psi_1
|
|
28
|
+
self.psi_2 = psi_2
|
|
29
|
+
self.psi_low = psi_low
|
|
30
|
+
self.psi_hi = psi_hi
|
|
31
|
+
self.Delta = Delta
|
|
32
|
+
self.omega = omega
|
|
33
|
+
self.max_bisects = max_bisects
|
|
34
|
+
|
|
35
|
+
def prepare_line_search(self):
|
|
36
|
+
super().prepare_line_search()
|
|
37
|
+
|
|
38
|
+
self.epsk = self.eps * abs(self.get_fg("f", 0.))
|
|
39
|
+
|
|
40
|
+
def bisect(self, a, b):
|
|
41
|
+
"""Bisect interval [a, b]."""
|
|
42
|
+
for i in range(self.max_bisects):
|
|
43
|
+
# U3 a.
|
|
44
|
+
d = (1 - self.theta)*a + self.theta*b
|
|
45
|
+
dphi_d = self.get_phi_dphi("g", d)
|
|
46
|
+
if dphi_d >= 0:
|
|
47
|
+
return a, d
|
|
48
|
+
|
|
49
|
+
phi_d = self.get_phi_dphi("f", d)
|
|
50
|
+
# U3 b.
|
|
51
|
+
# If (dphi_d > 0) we would already have returned above...
|
|
52
|
+
if phi_d <= self.phi0 + self.epsk:
|
|
53
|
+
a = d
|
|
54
|
+
# U3 c.
|
|
55
|
+
elif phi_d > self.phi0 + self.epsk:
|
|
56
|
+
b = d
|
|
57
|
+
raise Exception("Bisect failed!")
|
|
58
|
+
|
|
59
|
+
def interval_update(self, a, b, c):
|
|
60
|
+
"""Narrows down the bracketing interval."""
|
|
61
|
+
# U0
|
|
62
|
+
if not (a < c < b):
|
|
63
|
+
return a, b
|
|
64
|
+
|
|
65
|
+
phi_c, dphi_c = self.get_phi_dphi("fg", c)
|
|
66
|
+
# U1, sign of slope projection changed. We already passed the minimum.
|
|
67
|
+
if dphi_c >= 0:
|
|
68
|
+
return a, c
|
|
69
|
+
# U2, we are moving towards the minimum.
|
|
70
|
+
elif phi_c <= self.phi0 + self.epsk:
|
|
71
|
+
return c, b
|
|
72
|
+
|
|
73
|
+
# U3, phi_c increased above phi0, so we probably passed the minimum.
|
|
74
|
+
return self.bisect(a, c)
|
|
75
|
+
|
|
76
|
+
def secant(self, a, b):
|
|
77
|
+
"""Take secant step."""
|
|
78
|
+
dphia = self.get_phi_dphi("g", a)
|
|
79
|
+
dphib = self.get_phi_dphi("g", b)
|
|
80
|
+
return (a*dphib - b*dphia) / (dphib - dphia)
|
|
81
|
+
|
|
82
|
+
def double_secant(self, a, b):
|
|
83
|
+
"""Take secant² step."""
|
|
84
|
+
c = self.secant(a, b)
|
|
85
|
+
A, B = self.interval_update(a, b, c)
|
|
86
|
+
cB_close = np.isclose(c, B)
|
|
87
|
+
cA_close = np.isclose(c, A)
|
|
88
|
+
|
|
89
|
+
if cB_close:
|
|
90
|
+
c_dash = self.secant(b, B)
|
|
91
|
+
elif cA_close:
|
|
92
|
+
c_dash = self.secant(a, A)
|
|
93
|
+
|
|
94
|
+
if cB_close or cA_close:
|
|
95
|
+
a_dash, b_dash = self.interval_update(A, B, c_dash)
|
|
96
|
+
else:
|
|
97
|
+
a_dash, b_dash = A, B
|
|
98
|
+
return a_dash, b_dash
|
|
99
|
+
|
|
100
|
+
def bracket(self, c):
|
|
101
|
+
"""Generate initial interval [a, b] that satisfies the opposite
|
|
102
|
+
slope condition (dphi(a) < 0, dphi(b) > 0).
|
|
103
|
+
"""
|
|
104
|
+
cs = list()
|
|
105
|
+
p0epsk = self.phi0 + self.epsk
|
|
106
|
+
for j in range(10):
|
|
107
|
+
cs.append(c)
|
|
108
|
+
|
|
109
|
+
dphi_j = self.get_phi_dphi("g", c)
|
|
110
|
+
|
|
111
|
+
if (dphi_j >= 0) and (j == 0):
|
|
112
|
+
return 0, c
|
|
113
|
+
|
|
114
|
+
phi_j = self.get_phi_dphi("f", c)
|
|
115
|
+
if dphi_j >= 0:
|
|
116
|
+
phi_inds = np.array([self.get_fg("f", c) for c in cs[:-1]]) <= p0epsk
|
|
117
|
+
# See https://stackoverflow.com/a/8768734
|
|
118
|
+
ci = len(phi_inds) - phi_inds[::-1].argmax() - 1
|
|
119
|
+
return cs[ci], c
|
|
120
|
+
elif phi_j > p0epsk:
|
|
121
|
+
return self.bisect(0, c)
|
|
122
|
+
|
|
123
|
+
c *= self.rho
|
|
124
|
+
|
|
125
|
+
def norm_inf(self, arr):
|
|
126
|
+
"""Returns infinity norm of given array."""
|
|
127
|
+
return np.linalg.norm(arr, np.inf)
|
|
128
|
+
|
|
129
|
+
def initial(self):
|
|
130
|
+
"""Get an initial guess for alpha."""
|
|
131
|
+
if (~np.isclose(self.x0, np.zeros_like(self.x0))).any():
|
|
132
|
+
c = self.psi_0 * self.norm_inf(self.x0)/self.norm_inf(self.g0)
|
|
133
|
+
elif not np.isclose(self.f0, 0):
|
|
134
|
+
c = self.psi_0 * self.f0 / self.norm_inf(self.g0)**2
|
|
135
|
+
else:
|
|
136
|
+
c = 1
|
|
137
|
+
return c
|
|
138
|
+
|
|
139
|
+
def take_quad_step(self, alpha, g0_):
|
|
140
|
+
"""Try to get alpha for minimum step from quadratic interpolation."""
|
|
141
|
+
fact = max(self.psi_low, g0_/(self.dphi0*self.psi_2))
|
|
142
|
+
alpha_ = min(fact, self.psi_hi) * alpha
|
|
143
|
+
phi_ = self.get_phi_dphi("f", alpha_)
|
|
144
|
+
denom = 2*((phi_-self.phi0)/alpha_ - self.dphi0)
|
|
145
|
+
f_temp = self.get_fg("f", alpha_)
|
|
146
|
+
if denom > 0.:
|
|
147
|
+
c = -self.dphi0*alpha_ / denom
|
|
148
|
+
if f_temp > self.get_fg("f", 0):
|
|
149
|
+
c = max(c, alpha_*1e-10)
|
|
150
|
+
else:
|
|
151
|
+
c = alpha
|
|
152
|
+
return c
|
|
153
|
+
|
|
154
|
+
def run_line_search(self):
|
|
155
|
+
if self.alpha_init is None and self.alpha_prev:
|
|
156
|
+
alpha_init = self.alpha_prev
|
|
157
|
+
elif self.alpha_init is None and self.alpha_prev is None:
|
|
158
|
+
alpha_init = self.initial()
|
|
159
|
+
else:
|
|
160
|
+
alpha_init = self.alpha_init
|
|
161
|
+
|
|
162
|
+
if self.quad_step:
|
|
163
|
+
g0_ = -2*abs(self.get_fg("f", 0)/alpha_init) if (self.dphi0_prev is None) \
|
|
164
|
+
else self.dphi0_prev
|
|
165
|
+
alpha_init = self.take_quad_step(self.psi_2*alpha_init, g0_)
|
|
166
|
+
# This may raise LineSearchConverged
|
|
167
|
+
_ = self.get_phi_dphi("fg", alpha_init)
|
|
168
|
+
|
|
169
|
+
# TODO: cubic interpolation for better alpha_init
|
|
170
|
+
ak, bk = self.bracket(alpha_init)
|
|
171
|
+
for k in range(self.max_cycles):
|
|
172
|
+
if self.cond_func(ak):
|
|
173
|
+
break
|
|
174
|
+
# secant² step
|
|
175
|
+
a, b = self.double_secant(ak, bk)
|
|
176
|
+
if (b - a) > self.gamma*(bk - ak):
|
|
177
|
+
# Bisection step
|
|
178
|
+
c = (a + b)/2
|
|
179
|
+
a, b = self.interval_update(a, b, c)
|
|
180
|
+
ak, bk = a, b
|
|
181
|
+
else:
|
|
182
|
+
raise LineSearchNotConverged
|
|
183
|
+
|
|
184
|
+
return ak
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
from collections import namedtuple
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LineSearchConverged(Exception):
|
|
9
|
+
def __init__(self, alpha):
|
|
10
|
+
self.alpha = alpha
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LineSearchNotConverged(Exception):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
LineSearchResult = namedtuple(
|
|
18
|
+
"LineSearchResult",
|
|
19
|
+
"converged alpha f_new g_new f_evals df_evals dphi0",
|
|
20
|
+
# defaults=( None, None, None, None, None, None),
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LineSearch(metaclass=abc.ABCMeta):
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
p,
|
|
28
|
+
cond="armijo",
|
|
29
|
+
x0=None,
|
|
30
|
+
geometry=None,
|
|
31
|
+
f=None,
|
|
32
|
+
df=None,
|
|
33
|
+
alpha_init=None,
|
|
34
|
+
f0=None,
|
|
35
|
+
g0=None,
|
|
36
|
+
c1=0.1,
|
|
37
|
+
c2=0.9,
|
|
38
|
+
max_cycles=10,
|
|
39
|
+
alpha_min=1e-6,
|
|
40
|
+
):
|
|
41
|
+
self.p = p
|
|
42
|
+
self.geometry = geometry
|
|
43
|
+
self.f = f
|
|
44
|
+
self.df = df
|
|
45
|
+
|
|
46
|
+
geometry_supplied = self.geometry is not None
|
|
47
|
+
assert geometry_supplied or (
|
|
48
|
+
x0 is not None
|
|
49
|
+
), "Supply either 'geometry' or the starting coordinates 'x0'!"
|
|
50
|
+
assert geometry_supplied or (self.f and self.df), (
|
|
51
|
+
"Supply either 'geometry' with a calculator or the two functions "
|
|
52
|
+
"'f' and 'df' to calculate the energy and its gradient!"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self.x0 = x0
|
|
56
|
+
if self.geometry:
|
|
57
|
+
self.f = lambda coords: self.geometry.get_energy_at(coords)
|
|
58
|
+
self.df = lambda coords: -self.geometry.get_energy_and_forces_at(coords)[
|
|
59
|
+
"forces"
|
|
60
|
+
]
|
|
61
|
+
self.x0 = self.geometry.coords.copy()
|
|
62
|
+
|
|
63
|
+
self.alpha_init = alpha_init
|
|
64
|
+
self.f0 = f0
|
|
65
|
+
self.g0 = g0
|
|
66
|
+
self.c1 = c1
|
|
67
|
+
self.c2 = c2
|
|
68
|
+
self.max_cycles = max_cycles
|
|
69
|
+
self.alpha_min = alpha_min
|
|
70
|
+
|
|
71
|
+
# Store calculated energies & gradients
|
|
72
|
+
self.alpha_fs = {}
|
|
73
|
+
self.alpha_dfs = {}
|
|
74
|
+
self.f_evals = 0
|
|
75
|
+
self.df_evals = 0
|
|
76
|
+
|
|
77
|
+
self.dphis = {}
|
|
78
|
+
|
|
79
|
+
self.cond_funcs = {
|
|
80
|
+
"armijo": self.sufficiently_decreased,
|
|
81
|
+
"wolfe": self.wolfe_condition,
|
|
82
|
+
"strong_wolfe": self.strong_wolfe_condition,
|
|
83
|
+
}
|
|
84
|
+
self.can_eval_cond_funcs = {
|
|
85
|
+
"armijo": lambda alpha: alpha in self.alpha_fs,
|
|
86
|
+
"wolfe": self.got_alpha_phi_dphi,
|
|
87
|
+
"strong_wolfe": self.got_alpha_phi_dphi,
|
|
88
|
+
}
|
|
89
|
+
self.cond_func = self.cond_funcs[cond]
|
|
90
|
+
self.can_eval_cond_func = self.can_eval_cond_funcs[cond]
|
|
91
|
+
|
|
92
|
+
self.logger = logging.getLogger("optimizer")
|
|
93
|
+
|
|
94
|
+
def log(self, message):
|
|
95
|
+
self.logger.debug(message)
|
|
96
|
+
|
|
97
|
+
def prepare_line_search(self):
|
|
98
|
+
if self.f0 is None:
|
|
99
|
+
self.phi0 = self.get_phi_dphi("f", 0)
|
|
100
|
+
else:
|
|
101
|
+
self.phi0 = self.f0
|
|
102
|
+
self.alpha_fs[0.0] = self.f0
|
|
103
|
+
if self.g0 is None:
|
|
104
|
+
self.dphi0 = self.get_phi_dphi("g", 0)
|
|
105
|
+
else:
|
|
106
|
+
self.dphi0 = self.g0.dot(self.p)
|
|
107
|
+
self.alpha_dfs[0.0] = self.g0
|
|
108
|
+
|
|
109
|
+
def check_alpha(self, alpha):
|
|
110
|
+
if (alpha != 0.0) and (np.isnan(alpha) or (alpha < self.alpha_min)):
|
|
111
|
+
raise LineSearchNotConverged()
|
|
112
|
+
|
|
113
|
+
def _phi(self, alpha):
|
|
114
|
+
alpha = float(alpha)
|
|
115
|
+
self.check_alpha(alpha)
|
|
116
|
+
try:
|
|
117
|
+
f_alpha = self.alpha_fs[alpha]
|
|
118
|
+
except KeyError:
|
|
119
|
+
self.log(f"\tEvaluating energy for alpha={alpha:.6f}")
|
|
120
|
+
f_alpha = self.f(self.x0 + alpha * self.p)
|
|
121
|
+
self.f_evals += 1
|
|
122
|
+
self.alpha_fs[alpha] = f_alpha
|
|
123
|
+
return f_alpha
|
|
124
|
+
|
|
125
|
+
def _dphi(self, alpha):
|
|
126
|
+
alpha = float(alpha)
|
|
127
|
+
self.check_alpha(alpha)
|
|
128
|
+
try:
|
|
129
|
+
df_alpha = self.alpha_dfs[alpha]
|
|
130
|
+
dphi_ = df_alpha.dot(self.p)
|
|
131
|
+
except KeyError:
|
|
132
|
+
self.log(f"\tEvaluating gradient for alpha={alpha:.6f}")
|
|
133
|
+
df_alpha = self.df(self.x0 + alpha * self.p)
|
|
134
|
+
self.df_evals += 1
|
|
135
|
+
self.alpha_dfs[alpha] = df_alpha
|
|
136
|
+
dphi_ = df_alpha.dot(self.p)
|
|
137
|
+
self.dphis[alpha] = dphi_
|
|
138
|
+
return dphi_
|
|
139
|
+
|
|
140
|
+
def got_alpha_phi_dphi(self, alpha):
|
|
141
|
+
return (alpha in self.alpha_fs) and (alpha in self.alpha_dfs)
|
|
142
|
+
|
|
143
|
+
def get_phi_dphi(self, what, alpha, check=True):
|
|
144
|
+
"""Wrapper that handles function/gradient evaluations."""
|
|
145
|
+
alpha = float(alpha)
|
|
146
|
+
whats = "f g fg".split()
|
|
147
|
+
assert what in whats
|
|
148
|
+
calc_funcs = {
|
|
149
|
+
"f": self._phi,
|
|
150
|
+
"g": self._dphi,
|
|
151
|
+
}
|
|
152
|
+
result = [calc_funcs[w](alpha) for w in what]
|
|
153
|
+
# Check if we got both phi and dphi for alpha now. If so we
|
|
154
|
+
# can check if the chosen condition (Wolfe/approx. Wolfe) is
|
|
155
|
+
# satisfied.
|
|
156
|
+
if (
|
|
157
|
+
check
|
|
158
|
+
and (alpha > 0.0)
|
|
159
|
+
and self.can_eval_cond_func(alpha)
|
|
160
|
+
and self.cond_func(alpha)
|
|
161
|
+
):
|
|
162
|
+
self.log(f"Line search condition is satisfied for α={alpha:.6f}.")
|
|
163
|
+
raise LineSearchConverged(alpha)
|
|
164
|
+
# Dont return a list if only f or g was requested.
|
|
165
|
+
if len(what) == 1:
|
|
166
|
+
result = result[0]
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
def get_fg(self, what, alpha):
|
|
170
|
+
"""Lookup raw function/gradient values for a given alpha."""
|
|
171
|
+
whats = "f g fg".split()
|
|
172
|
+
assert what in whats
|
|
173
|
+
lookups = {
|
|
174
|
+
"f": self.alpha_fs,
|
|
175
|
+
"g": self.alpha_dfs,
|
|
176
|
+
}
|
|
177
|
+
result = [lookups[w][alpha] for w in what]
|
|
178
|
+
if len(what) == 1:
|
|
179
|
+
result = result[0]
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
def sufficiently_decreased(self, alpha):
|
|
183
|
+
"""Sufficient decrease/Armijo condition."""
|
|
184
|
+
return self._phi(alpha) <= (self.phi0 + self.c1 * alpha * self.dphi0)
|
|
185
|
+
|
|
186
|
+
def curvature_condition(self, alpha):
|
|
187
|
+
return self._dphi(alpha) >= self.c2 * self.dphi0
|
|
188
|
+
|
|
189
|
+
def strong_curvature_condition(self, alpha):
|
|
190
|
+
return abs(self._dphi(alpha)) <= -self.c2 * self.dphi0
|
|
191
|
+
|
|
192
|
+
def wolfe_condition(self, alpha):
|
|
193
|
+
"""Normal, not strong, Wolfe condition."""
|
|
194
|
+
return self.sufficiently_decreased(alpha) and self.curvature_condition(alpha)
|
|
195
|
+
|
|
196
|
+
def strong_wolfe_condition(self, alpha):
|
|
197
|
+
"""Strong wolfe condition."""
|
|
198
|
+
return self.sufficiently_decreased(alpha) and self.strong_curvature_condition(
|
|
199
|
+
alpha
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
@abc.abstractmethod
|
|
203
|
+
def run_line_search(self):
|
|
204
|
+
raise NotImplementedError
|
|
205
|
+
|
|
206
|
+
def run(self):
|
|
207
|
+
self.prepare_line_search()
|
|
208
|
+
|
|
209
|
+
# Normal termination
|
|
210
|
+
try:
|
|
211
|
+
self.log(f"Starting {self.__class__.__name__} line search.")
|
|
212
|
+
alpha = self.run_line_search()
|
|
213
|
+
# Termination in get_phi_dphi
|
|
214
|
+
except LineSearchConverged as lsc:
|
|
215
|
+
alpha = lsc.alpha
|
|
216
|
+
# Failed LineSearch
|
|
217
|
+
except LineSearchNotConverged:
|
|
218
|
+
alpha = None
|
|
219
|
+
|
|
220
|
+
result = LineSearchResult(
|
|
221
|
+
converged=bool(alpha),
|
|
222
|
+
alpha=alpha,
|
|
223
|
+
# The gradient at the endpoint of the line search may not
|
|
224
|
+
# have been evaluted, but the function value was always
|
|
225
|
+
# evaluated, except when the line search did not converge.
|
|
226
|
+
f_new=self.alpha_fs.get(alpha, None),
|
|
227
|
+
g_new=self.alpha_dfs.get(alpha, None),
|
|
228
|
+
f_evals=self.f_evals,
|
|
229
|
+
df_evals=self.df_evals,
|
|
230
|
+
dphi0=self.dphi0,
|
|
231
|
+
)
|
|
232
|
+
return result
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# [1] Nocedal, Numerical Optimization
|
|
2
|
+
|
|
3
|
+
from pysisyphus.line_searches.LineSearch import LineSearch, \
|
|
4
|
+
LineSearchNotConverged
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from pysisyphus.line_searches.interpol import interpol_alpha_quad, interpol_alpha_cubic
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StrongWolfe(LineSearch):
|
|
11
|
+
|
|
12
|
+
def __init__(self, *args, alpha_max=10., fac=2, **kwargs):
|
|
13
|
+
"""Wolfe line search.
|
|
14
|
+
|
|
15
|
+
Uses only energy & gradient evaluations.
|
|
16
|
+
|
|
17
|
+
See [1], Chapter 3, Line Search methods, Section 3.5 p. 60."""
|
|
18
|
+
|
|
19
|
+
kwargs["cond"] = "strong_wolfe"
|
|
20
|
+
super().__init__(*args, **kwargs)
|
|
21
|
+
|
|
22
|
+
self.alpha_max = float(alpha_max)
|
|
23
|
+
self.fac = fac
|
|
24
|
+
|
|
25
|
+
def zoom(self, alpha_lo, alpha_hi, phi_lo,
|
|
26
|
+
phi_alpha_=None, alpha_0_=None, max_cycles=10):
|
|
27
|
+
phi0, dphi0 = self.get_phi_dphi("fg", 0)
|
|
28
|
+
|
|
29
|
+
alphas = list()
|
|
30
|
+
phi_alphas = list()
|
|
31
|
+
if phi_alpha_:
|
|
32
|
+
phi_alphas = [phi_alpha_, ]
|
|
33
|
+
if alpha_0_:
|
|
34
|
+
alphas = [alpha_0_, ]
|
|
35
|
+
|
|
36
|
+
for j in range(max_cycles):
|
|
37
|
+
# Interpoaltion of alpha between alpha_lo, alpha_hi
|
|
38
|
+
#
|
|
39
|
+
# Try cubic interpolation if at least two additional alphas and
|
|
40
|
+
# corresponding phi_alpha values are available beside alpha = 0.
|
|
41
|
+
if len(phi_alphas) > 1:
|
|
42
|
+
alpha_prev = alphas[-1]
|
|
43
|
+
phi_alpha_prev = phi_alphas[-1]
|
|
44
|
+
alpha_j = interpol_alpha_cubic(phi0, dphi0,
|
|
45
|
+
phi_alpha_, phi_alpha_prev,
|
|
46
|
+
alpha_0_, alpha_prev
|
|
47
|
+
)
|
|
48
|
+
# Try quadratic interpolation if only one additional alpha and
|
|
49
|
+
# corresponding phi_alpha value are available beside alpha = 0.
|
|
50
|
+
elif len(phi_alphas) == 1:
|
|
51
|
+
alpha_j = interpol_alpha_quad(phi0, dphi0, phi_alpha_, alpha_0_)
|
|
52
|
+
# Fallback to simple bisection
|
|
53
|
+
else:
|
|
54
|
+
alpha_j = (alpha_lo + alpha_hi) / 2
|
|
55
|
+
|
|
56
|
+
phi_j = self.get_phi_dphi("f", alpha_j)
|
|
57
|
+
# Store the values so they can be reused for cubic interpolation
|
|
58
|
+
alphas.append(alpha_j)
|
|
59
|
+
phi_alphas.append(phi_j)
|
|
60
|
+
|
|
61
|
+
# True if alpha is still too big or if the function value
|
|
62
|
+
# increased compared to the previous cycle.
|
|
63
|
+
if not self.sufficiently_decreased(alpha_j) or (phi_j > phi_lo):
|
|
64
|
+
# Shrink interval to (alpha_lo, alpha_j)
|
|
65
|
+
alpha_hi = alpha_j
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
# If line search converged LineSearchConverged will be raised in
|
|
69
|
+
# this call.
|
|
70
|
+
dphi_j = self.get_phi_dphi("g", alpha_j)
|
|
71
|
+
|
|
72
|
+
if (dphi_j * (alpha_hi - alpha_lo)) >= 0:
|
|
73
|
+
alpha_hi = alpha_lo
|
|
74
|
+
# Shrink interval to (alpha_j, alpha_hi)
|
|
75
|
+
alpha_lo = alpha_j
|
|
76
|
+
|
|
77
|
+
def run_line_search(self):
|
|
78
|
+
phi0, dphi0 = self.get_phi_dphi("fg", 0)
|
|
79
|
+
|
|
80
|
+
alpha_prev = 0
|
|
81
|
+
phi_prev = phi0
|
|
82
|
+
if self.alpha_init is not None:
|
|
83
|
+
alpha_i = self.alpha_init
|
|
84
|
+
else:
|
|
85
|
+
alpha_i = 1.0
|
|
86
|
+
|
|
87
|
+
for i in range(self.max_cycles):
|
|
88
|
+
phi_i = self.get_phi_dphi("f", alpha_i)
|
|
89
|
+
phi_rose = (phi_i >= phi_prev)
|
|
90
|
+
# In [1] this condition is given with (if not sufficiently_decreased ...)
|
|
91
|
+
# I guess this may give problems if the initial step is too small; then
|
|
92
|
+
# suff. decr. is also not fullfilled ...
|
|
93
|
+
if not self.sufficiently_decreased(alpha_i) or (phi_rose and i > 0):
|
|
94
|
+
return self.zoom(alpha_prev, alpha_i, phi_prev, phi_i, alpha_i)
|
|
95
|
+
|
|
96
|
+
dphi_i = self.get_phi_dphi("g", alpha_i)
|
|
97
|
+
if self.strong_curvature_condition(alpha_i):
|
|
98
|
+
break
|
|
99
|
+
|
|
100
|
+
if dphi_i >= 0:
|
|
101
|
+
return self.zoom(alpha_i, alpha_prev, phi_i, phi_alpha_=phi_i, alpha_0_=alpha_i)
|
|
102
|
+
alpha_prev = alpha_i
|
|
103
|
+
alpha_i = min(self.fac * alpha_i, self.alpha_max)
|
|
104
|
+
# Premature abort of the loop may happen through LineSearchConverged being raised
|
|
105
|
+
else:
|
|
106
|
+
raise LineSearchNotConverged
|
|
107
|
+
|
|
108
|
+
return alpha_i
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def interpol_alpha_quad(f_0, df_0, f_alpha_0, alpha_0):
|
|
4
|
+
return -df_0*alpha_0**2 / 2 / (f_alpha_0 - f_0 - df_0*alpha_0)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def interpol_alpha_cubic(f_0, df_0, f_alpha_0, f_alpha_1, alpha_0, alpha_1):
|
|
8
|
+
quot = 1 / (alpha_0**2 * alpha_1**2 * (alpha_1 - alpha_0))
|
|
9
|
+
A = np.array(((alpha_0**2, -alpha_1**2),
|
|
10
|
+
(-alpha_0**3, alpha_1**3)))
|
|
11
|
+
B = np.array(((f_alpha_1 - f_0 - df_0*alpha_1),
|
|
12
|
+
(f_alpha_0 - f_0 - df_0*alpha_0)))
|
|
13
|
+
a, b = quot * A @ B
|
|
14
|
+
alpha_cubic = (-b + (b**2 - 3*a*df_0)**(0.5)) / (3*a)
|
|
15
|
+
return alpha_cubic
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from math import sqrt
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NormalMode:
|
|
7
|
+
"""See http://gaussian.com/vib/"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, l, masses):
|
|
10
|
+
"""NormalMode class.
|
|
11
|
+
|
|
12
|
+
Cartesian displacements are normalized to 1.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
l : np.array
|
|
17
|
+
Cartesian, non-mass-weighted displacements.
|
|
18
|
+
masses : np.array
|
|
19
|
+
Atomic masses.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
self.l = np.array(l.flatten())
|
|
23
|
+
self.l /= np.linalg.norm(l)
|
|
24
|
+
self.masses = masses
|
|
25
|
+
assert self.l.shape == self.masses.shape
|
|
26
|
+
|
|
27
|
+
def __len__(self):
|
|
28
|
+
return self.l.size
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def red_mass(self):
|
|
32
|
+
return 1 / np.sum(np.square(self.l_mw) / self.masses)
|
|
33
|
+
|
|
34
|
+
def mw_norm_for_norm(self, norm=0.01):
|
|
35
|
+
return norm * sqrt(self.red_mass)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def l_mw(self):
|
|
39
|
+
l_mw = self.l * np.sqrt(self.masses)
|
|
40
|
+
return l_mw / np.linalg.norm(l_mw)
|