| 1 | /* | 
|---|
| 2 | * Project: MoleCuilder | 
|---|
| 3 | * Description: creates and alters molecular systems | 
|---|
| 4 | * Copyright (C)  2012 University of Bonn. All rights reserved. | 
|---|
| 5 | * Please see the COPYING file or "Copyright notice" in builder.cpp for details. | 
|---|
| 6 | * | 
|---|
| 7 | * | 
|---|
| 8 | *   This file is part of MoleCuilder. | 
|---|
| 9 | * | 
|---|
| 10 | *    MoleCuilder is free software: you can redistribute it and/or modify | 
|---|
| 11 | *    it under the terms of the GNU General Public License as published by | 
|---|
| 12 | *    the Free Software Foundation, either version 2 of the License, or | 
|---|
| 13 | *    (at your option) any later version. | 
|---|
| 14 | * | 
|---|
| 15 | *    MoleCuilder is distributed in the hope that it will be useful, | 
|---|
| 16 | *    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 17 | *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 18 | *    GNU General Public License for more details. | 
|---|
| 19 | * | 
|---|
| 20 | *    You should have received a copy of the GNU General Public License | 
|---|
| 21 | *    along with MoleCuilder.  If not, see <http://www.gnu.org/licenses/>. | 
|---|
| 22 | */ | 
|---|
| 23 |  | 
|---|
| 24 | /* | 
|---|
| 25 | * LevMartester.cpp | 
|---|
| 26 | * | 
|---|
| 27 | *  Created on: Sep 27, 2012 | 
|---|
| 28 | *      Author: heber | 
|---|
| 29 | */ | 
|---|
| 30 |  | 
|---|
| 31 |  | 
|---|
| 32 | // include config.h | 
|---|
| 33 | #ifdef HAVE_CONFIG_H | 
|---|
| 34 | #include <config.h> | 
|---|
| 35 | #endif | 
|---|
| 36 |  | 
|---|
| 37 | #include "CodePatterns/MemDebug.hpp" | 
|---|
| 38 |  | 
|---|
| 39 | #include <boost/archive/text_iarchive.hpp> | 
|---|
| 40 | #include <boost/filesystem.hpp> | 
|---|
| 41 | #include <boost/program_options.hpp> | 
|---|
| 42 |  | 
|---|
| 43 | #include <fstream> | 
|---|
| 44 | #include <iostream> | 
|---|
| 45 | #include <iterator> | 
|---|
| 46 | #include <vector> | 
|---|
| 47 |  | 
|---|
| 48 | #include <levmar.h> | 
|---|
| 49 |  | 
|---|
| 50 | #include "CodePatterns/Assert.hpp" | 
|---|
| 51 | #include "CodePatterns/Log.hpp" | 
|---|
| 52 |  | 
|---|
| 53 | #include "LinearAlgebra/Vector.hpp" | 
|---|
| 54 |  | 
|---|
| 55 | #include "Fragmentation/Homology/HomologyContainer.hpp" | 
|---|
| 56 | #include "Fragmentation/SetValues/Fragment.hpp" | 
|---|
| 57 |  | 
|---|
| 58 | namespace po = boost::program_options; | 
|---|
| 59 |  | 
|---|
| 60 | HomologyGraph getFirstGraphWithTwoCarbons(const HomologyContainer &homologies) | 
|---|
| 61 | { | 
|---|
| 62 | FragmentNode SaturatedCarbon(6,4); // carbon has atomic number 6 and should have 4 bonds for C2H6 | 
|---|
| 63 | for (HomologyContainer::container_t::const_iterator iter = | 
|---|
| 64 | homologies.begin(); iter != homologies.end(); ++iter) { | 
|---|
| 65 | if (iter->first.hasNode(SaturatedCarbon,2)) | 
|---|
| 66 | return iter->first; | 
|---|
| 67 | } | 
|---|
| 68 | return HomologyGraph(); | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | /* Meyer's (reformulated) problem, minimum at (2.48, 6.18, 3.45) */ | 
|---|
| 72 | void meyer(double *p, double *x, int m, int n, void *data) | 
|---|
| 73 | { | 
|---|
| 74 | register int i; | 
|---|
| 75 | double ui; | 
|---|
| 76 |  | 
|---|
| 77 | for(i=0; i<n; ++i){ | 
|---|
| 78 | ui=0.45+0.05*i; | 
|---|
| 79 | x[i]=p[0]*exp(10.0*p[1]/(ui+p[2]) - 13.0); | 
|---|
| 80 | } | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | void jacmeyer(double *p, double *jac, int m, int n, void *data) | 
|---|
| 84 | { | 
|---|
| 85 | register int i, j; | 
|---|
| 86 | double ui, tmp; | 
|---|
| 87 |  | 
|---|
| 88 | for(i=j=0; i<n; ++i){ | 
|---|
| 89 | ui=0.45+0.05*i; | 
|---|
| 90 | tmp=exp(10.0*p[1]/(ui+p[2]) - 13.0); | 
|---|
| 91 |  | 
|---|
| 92 | jac[j++]=tmp; | 
|---|
| 93 | jac[j++]=10.0*p[0]*tmp/(ui+p[2]); | 
|---|
| 94 | jac[j++]=-10.0*p[0]*p[1]*tmp/((ui+p[2])*(ui+p[2])); | 
|---|
| 95 | } | 
|---|
| 96 | } | 
|---|
| 97 |  | 
|---|
| 98 |  | 
|---|
| 99 | int main(int argc, char **argv) | 
|---|
| 100 | { | 
|---|
| 101 | std::cout << "Hello to the World from LevMar!" << std::endl; | 
|---|
| 102 |  | 
|---|
| 103 | // load homology file | 
|---|
| 104 | po::options_description desc("Allowed options"); | 
|---|
| 105 | desc.add_options() | 
|---|
| 106 | ("help", "produce help message") | 
|---|
| 107 | ("homology-file", po::value< boost::filesystem::path >(), "homology file to parse") | 
|---|
| 108 | ; | 
|---|
| 109 |  | 
|---|
| 110 | po::variables_map vm; | 
|---|
| 111 | po::store(po::parse_command_line(argc, argv, desc), vm); | 
|---|
| 112 | po::notify(vm); | 
|---|
| 113 |  | 
|---|
| 114 | if (vm.count("help")) { | 
|---|
| 115 | std::cout << desc << "\n"; | 
|---|
| 116 | return 1; | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | boost::filesystem::path homology_file; | 
|---|
| 120 | if (vm.count("homology-file")) { | 
|---|
| 121 | homology_file = vm["homology-file"].as<boost::filesystem::path>(); | 
|---|
| 122 | LOG(1, "INFO: Parsing " << homology_file.string() << "."); | 
|---|
| 123 | } else { | 
|---|
| 124 | LOG(0, "homology-file level was not set."); | 
|---|
| 125 | } | 
|---|
| 126 | HomologyContainer homologies; | 
|---|
| 127 | if (boost::filesystem::exists(homology_file)) { | 
|---|
| 128 | std::ifstream returnstream(homology_file.string().c_str()); | 
|---|
| 129 | if (returnstream.good()) { | 
|---|
| 130 | boost::archive::text_iarchive ia(returnstream); | 
|---|
| 131 | ia >> homologies; | 
|---|
| 132 | } else { | 
|---|
| 133 | ELOG(2, "Failed to parse from " << homology_file.string() << "."); | 
|---|
| 134 | } | 
|---|
| 135 | returnstream.close(); | 
|---|
| 136 | } else { | 
|---|
| 137 | ELOG(0, homology_file << " does not exist."); | 
|---|
| 138 | } | 
|---|
| 139 |  | 
|---|
| 140 | // first we try to look into the HomologyContainer | 
|---|
| 141 | LOG(1, "INFO: Listing all present homologies ..."); | 
|---|
| 142 | for (HomologyContainer::container_t::const_iterator iter = | 
|---|
| 143 | homologies.begin(); iter != homologies.end(); ++iter) { | 
|---|
| 144 | LOG(1, "INFO: graph " << iter->first << " has Fragment " | 
|---|
| 145 | << iter->second.first << " and associated energy " << iter->second.second << "."); | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | // then we ought to pick the right HomologyGraph ... | 
|---|
| 149 | const HomologyGraph graph = getFirstGraphWithTwoCarbons(homologies); | 
|---|
| 150 | LOG(1, "First representative graph containing two saturated carbons is " << graph << "."); | 
|---|
| 151 |  | 
|---|
| 152 | // Afterwards we go through all of this type and gather the distance and the energy value | 
|---|
| 153 | typedef std::vector< std::pair<double, double> > DistanceEnergyVector_t; | 
|---|
| 154 | DistanceEnergyVector_t DistanceEnergyVector; | 
|---|
| 155 | std::pair<HomologyContainer::const_iterator, HomologyContainer::const_iterator> range = | 
|---|
| 156 | homologies.getHomologousGraphs(graph); | 
|---|
| 157 | for (HomologyContainer::const_iterator iter = range.first; iter != range.second; ++iter) { | 
|---|
| 158 | // get distance out of Fragment | 
|---|
| 159 | const Fragment &fragment = iter->second.first; | 
|---|
| 160 | const Fragment::charges_t charges = fragment.getCharges(); | 
|---|
| 161 | const Fragment::positions_t positions = fragment.getPositions(); | 
|---|
| 162 | std::vector< Vector > DistanceVectors; | 
|---|
| 163 | for (Fragment::charges_t::const_iterator chargeiter = charges.begin(); | 
|---|
| 164 | chargeiter != charges.end(); ++chargeiter) { | 
|---|
| 165 | if (*chargeiter == 6) { | 
|---|
| 166 | Fragment::positions_t::const_iterator positer = positions.begin(); | 
|---|
| 167 | const size_t steps = std::distance(charges.begin(), chargeiter); | 
|---|
| 168 | std::advance(positer, steps); | 
|---|
| 169 | DistanceVectors.push_back(Vector((*positer)[0], (*positer)[1], (*positer)[2])); | 
|---|
| 170 | } | 
|---|
| 171 | } | 
|---|
| 172 | ASSERT( DistanceVectors.size() == (size_t)2, | 
|---|
| 173 | "main() - found not exactly two carbon atoms in fragment."); | 
|---|
| 174 | const double distance = DistanceVectors[0].distance(DistanceVectors[1]); | 
|---|
| 175 | const double energy = iter->second.second; | 
|---|
| 176 | DistanceEnergyVector.push_back( std::make_pair(distance, energy) ); | 
|---|
| 177 | } | 
|---|
| 178 | LOG(1, "INFO: I gathered the following " << DistanceEnergyVector.size() << " data pairs: "); | 
|---|
| 179 | for (DistanceEnergyVector_t::const_iterator dataiter = DistanceEnergyVector.begin(); | 
|---|
| 180 | dataiter != DistanceEnergyVector.end(); ++dataiter) { | 
|---|
| 181 | LOG(1, "INFO: " << dataiter->first << " with energy " << dataiter->second); | 
|---|
| 182 | } | 
|---|
| 183 | // NOTICE that distance are in bohrradi as they come from MPQC! | 
|---|
| 184 |  | 
|---|
| 185 | // let levmar optimize | 
|---|
| 186 | register int i, j; | 
|---|
| 187 | int ret; | 
|---|
| 188 | double p[5], // 5 is max(2, 3, 5) | 
|---|
| 189 | x[16]; // 16 is max(2, 3, 5, 6, 16) | 
|---|
| 190 | int m, n; | 
|---|
| 191 | double opts[LM_OPTS_SZ], info[LM_INFO_SZ]; | 
|---|
| 192 |  | 
|---|
| 193 | opts[0]=LM_INIT_MU; opts[1]=1E-15; opts[2]=1E-15; opts[3]=1E-20; | 
|---|
| 194 | opts[4]= LM_DIFF_DELTA; // relevant only if the Jacobian is approximated using finite differences; specifies forward differencing | 
|---|
| 195 | //opts[4]=-LM_DIFF_DELTA; // specifies central differencing to approximate Jacobian; more accurate but more expensive to compute! | 
|---|
| 196 |  | 
|---|
| 197 | m=3; n=16; | 
|---|
| 198 | p[0]=8.85; p[1]=4.0; p[2]=2.5; | 
|---|
| 199 | x[0]=34.780;        x[1]=28.610; x[2]=23.650; x[3]=19.630; | 
|---|
| 200 | x[4]=16.370;        x[5]=13.720; x[6]=11.540; x[7]=9.744; | 
|---|
| 201 | x[8]=8.261; x[9]=7.030; x[10]=6.005; x[11]=5.147; | 
|---|
| 202 | x[12]=4.427;        x[13]=3.820; x[14]=3.307; x[15]=2.872; | 
|---|
| 203 | ret=dlevmar_der(meyer, jacmeyer, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian | 
|---|
| 204 |  | 
|---|
| 205 | { double *work, *covar; | 
|---|
| 206 | work=(double *)malloc((LM_DIF_WORKSZ(m, n)+m*m)*sizeof(double)); | 
|---|
| 207 | if(!work){ | 
|---|
| 208 | fprintf(stderr, "memory allocation request failed in main()\n"); | 
|---|
| 209 | exit(1); | 
|---|
| 210 | } | 
|---|
| 211 | covar=work+LM_DIF_WORKSZ(m, n); | 
|---|
| 212 |  | 
|---|
| 213 | ret=dlevmar_dif(meyer, p, x, m, n, 1000, opts, info, work, covar, NULL); // no Jacobian, caller allocates work memory, covariance estimated | 
|---|
| 214 |  | 
|---|
| 215 | printf("Covariance of the fit:\n"); | 
|---|
| 216 | for(i=0; i<m; ++i){ | 
|---|
| 217 | for(j=0; j<m; ++j) | 
|---|
| 218 | printf("%g ", covar[i*m+j]); | 
|---|
| 219 | printf("\n"); | 
|---|
| 220 | } | 
|---|
| 221 | printf("\n"); | 
|---|
| 222 |  | 
|---|
| 223 | free(work); | 
|---|
| 224 | } | 
|---|
| 225 |  | 
|---|
| 226 | //  { | 
|---|
| 227 | //   double err[16]; | 
|---|
| 228 | //   dlevmar_chkjac(meyer, jacmeyer, p, m, n, NULL, err); | 
|---|
| 229 | //   for(i=0; i<n; ++i) printf("gradient %d, err %g\n", i, err[i]); | 
|---|
| 230 | //  } | 
|---|
| 231 |  | 
|---|
| 232 | printf("Levenberg-Marquardt returned %d in %g iter, reason %g\nSolution: ", ret, info[5], info[6]); | 
|---|
| 233 | for(i=0; i<m; ++i) | 
|---|
| 234 | printf("%.7g ", p[i]); | 
|---|
| 235 | printf("\n\nMinimization info:\n"); | 
|---|
| 236 | for(i=0; i<LM_INFO_SZ; ++i) | 
|---|
| 237 | printf("%g ", info[i]); | 
|---|
| 238 | printf("\n"); | 
|---|
| 239 |  | 
|---|
| 240 | return 0; | 
|---|
| 241 | } | 
|---|