46 Commits

Author SHA1 Message Date
Daniel Bauer
35ca1b1317 version up 2021-07-21 16:47:32 +02:00
Daniel Bauer
d9a219a36a better verbose output during dataset generation and flag to ignore empty histograms 2021-07-21 16:45:58 +02:00
Daniel Bauer
2c565dee28 remove some debug statements 2021-07-19 08:32:57 +02:00
Daniel Bauer
c05becea22 version up 2021-07-17 14:44:09 +02:00
Daniel Bauer
c27dbb7ab5 test g=1 for correlation 2021-07-17 14:42:21 +02:00
Daniel Bauer
c9e18ebda9 Squashed commit of the following:
commit eaebf0dcbb259decbc0d8f5bbffa62244303f6c7
Author: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
Date:   Sat Jul 17 14:12:30 2021 +0200

    error for empty timeseries

commit 1fe5383c5079ca6c0ad102d4e2cb32d5b1227e80
Author: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
Date:   Sat Jul 17 14:09:13 2021 +0200

    refractored convdt slices calculation

commit 0b1fb7fb6a72edc50b013b59623551b2ccab6913
Author: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
Date:   Sat Jul 17 13:27:59 2021 +0200

    fix histogram building without convdt

commit f9882ca4cece57451cd2971d0993a2d3621b0cdf
Author: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
Date:   Sat Jul 17 12:53:06 2021 +0200

    fix tests not compiling

commit e6550e20bde3824f8199432b08777be41fc79fa8
Author: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
Date:   Sat Jul 17 12:43:36 2021 +0200

    refractoring

commit 18c77a9b6694d491ef23cebb3918ccda8fcc44a5
Author: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
Date:   Fri Jul 16 08:16:34 2021 +0200

    run and output for multiple datasets

commit 069f318f72207c416387007435eeff9867494e7a
Author: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
Date:   Fri Jul 16 07:58:01 2021 +0200

    cleanup io.rs

commit 10efa428a0c6bf490c9f2d7a4c1df185de402a11
Author: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
Date:   Thu Jul 15 19:27:22 2021 +0200

    parse multiple datasets with convdt
2021-07-17 14:14:25 +02:00
Daniel Bauer
0b5d0ebecd test time boundaries 2021-07-17 13:31:36 +02:00
Daniel Bauer
091b1f1382 TODO 2021-07-17 13:04:42 +02:00
Daniel Bauer
9849c00321 cargo ignore examples 2021-07-17 11:30:42 +02:00
daniel
034586d08e inlined autocorrelation calculation for better performance 2020-10-26 11:24:48 +01:00
Daniel Bauer
cc4448f1d8 Update README.md 2020-10-26 10:53:57 +01:00
Daniel Bauer
5ca708abf1 version up 2020-10-26 10:52:22 +01:00
Daniel Bauer
31ffb5958d Merge branch 'corr' of github.com:danijoo/WHAM into corr 2020-10-26 10:46:43 +01:00
Daniel Bauer
ffab55e0e1 version up 2020-10-26 10:46:23 +01:00
Daniel Bauer
69d0c7ca00 better output for uncorr 2020-10-26 10:45:22 +01:00
Daniel Bauer
8a88d7742a remove gsl 2020-10-26 10:20:41 +01:00
Daniel Bauer
18debf1a28 Update README.md 2020-10-25 22:48:38 +01:00
Daniel Bauer
a856e5d34f Update cli.yml 2020-10-25 22:41:05 +01:00
Daniel Bauer
2cc34919b0 Autocorrelation (#2)
* calculate autocorrelation stats_ineff and tau
* uncorr flag
* read timeseries into vector
* uncorrelate data
* README
* Update README.md
* Update README.md

Co-authored-by: Daniel Bauer <bauer@cbs.tu-darmstadt.de>
2020-10-25 22:40:13 +01:00
Daniel Bauer
cd57289211 Update README.md 2020-10-25 22:39:15 +01:00
Daniel Bauer
9d8423bce9 Update README.md 2020-10-25 22:36:27 +01:00
Daniel Bauer
2876c1dc3a README 2020-10-25 20:49:52 +01:00
Daniel Bauer
b9d08ae24b uncorrelate data 2020-10-25 20:36:02 +01:00
Daniel Bauer
07fc8344fe read timeseries into vector 2020-10-25 19:42:33 +01:00
Daniel Bauer
d7eea7aa03 uncorr flag 2020-10-25 19:07:33 +01:00
Daniel Bauer
3d93693eac calculate autocorrelation stats_ineff and tau 2020-10-25 18:38:25 +01:00
Daniel Bauer
38c30baa5b category 2020-10-24 20:53:07 +02:00
Daniel Bauer
b130596fe8 Merge branch 'master' of github.com:danijoo/WHAM 2020-10-24 20:44:30 +02:00
Daniel Bauer
05451a6cb2 cli 2020-10-24 20:44:27 +02:00
Daniel Bauer
bf8bf39319 edit todo 2020-10-24 20:44:18 +02:00
Daniel Bauer
6c63eda769 Update README.md 2020-10-24 20:43:54 +02:00
Daniel Bauer
1bbafeba70 lock update 2020-10-24 20:38:37 +02:00
Daniel Bauer
64f468e636 version up 2020-10-24 20:35:24 +02:00
Daniel Bauer
2f93b77879 update rand lib 2020-10-24 20:35:06 +02:00
Daniel Bauer
b148998b11 update GSL version 2020-10-24 20:21:32 +02:00
Daniel Bauer
87f3cceb6a keywords,categories 2020-10-24 20:06:28 +02:00
Daniel Bauer
7b39d603bd remove unneccessary parameter in bootstrapping function 2020-09-15 15:32:05 +02:00
Daniel Bauer
465f46b280 fix unit tests 2020-09-15 15:20:40 +02:00
Daniel Bauer
d221fc7195 lock 2020-09-15 14:49:03 +02:00
Daniel Bauer
dc218df051 calculate SE of A directly 2020-09-15 14:48:36 +02:00
Daniel Bauer
1e27cfd457 report standard error (SE) instead of standard deviations 2020-09-15 14:10:48 +02:00
Daniel Bauer
7116142c2a .gitignore 2020-03-11 09:40:31 +01:00
Daniel Bauer
fbdb451f6b print less 2020-03-11 09:40:01 +01:00
Daniel Bauer
d4119b2fae clean code (2) 2020-03-11 09:31:48 +01:00
Daniel Bauer
b9f6b24b20 clean code 2020-03-10 09:44:32 +01:00
Daniel Bauer
5390b089ac remove some code smell 2020-03-09 10:05:36 +01:00
16 changed files with 1163 additions and 461 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
target
reference
.idea
*bench.*
*bench.*
*.sublime*

290
Cargo.lock generated
View File

@@ -1,16 +1,20 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "GSL"
version = "1.1.0"
name = "addr2line"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7830156ea389bcbbdc8f01bf140b609b892bf7cbd0ec6ccf9957ea2be6f25ad3"
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
dependencies = [
"c_vec",
"libc",
"pkg-config",
"gimli",
]
[[package]]
name = "adler"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "ansi_term"
version = "0.11.0"
@@ -20,6 +24,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "assert_approx_eq"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd"
[[package]]
name = "atty"
version = "0.2.14"
@@ -33,50 +43,30 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.45"
version = "0.3.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8"
checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e"
dependencies = [
"backtrace-sys",
"cfg-if",
"addr2line",
"cfg-if 1.0.0",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "backtrace-sys"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17b52e737c40a7d75abca20b29a19a0eb7ba9fc72c5a72dd282a0a3c2c0dc35"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "c_vec"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9e1d9f7d49e289f36f19effbf3d5a5e30163ecf9c7a3c9be94d5374dec5b9a"
[[package]]
name = "cc"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
[[package]]
name = "cfg-if"
version = "0.1.10"
@@ -84,10 +74,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clap"
version = "2.33.0"
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
@@ -100,88 +96,96 @@ dependencies = [
]
[[package]]
name = "cloudabi"
version = "0.0.3"
name = "const_fn"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2"
[[package]]
name = "crossbeam-channel"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 1.0.0",
"const_fn",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 1.0.0",
"const_fn",
"lazy_static",
]
[[package]]
name = "either"
version = "1.5.3"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "error-chain"
version = "0.12.2"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
"backtrace",
"version_check",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if 0.1.10",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "hermit-abi"
version = "0.1.8"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
dependencies = [
"libc",
]
@@ -194,75 +198,99 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.67"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]]
name = "memoffset"
version = "0.5.3"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
dependencies = [
"rustc_version",
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "pkg-config"
version = "0.3.17"
name = "object"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
[[package]]
name = "ppv-lite86"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
[[package]]
name = "rand"
version = "0.5.6"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"cloudabi",
"fuchsia-cprng",
"getrandom",
"libc",
"rand_core 0.3.1",
"winapi",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_core"
version = "0.3.1"
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"rand_core 0.4.2",
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.4.2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rayon"
version = "1.3.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
@@ -270,12 +298,12 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.7.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-queue",
"crossbeam-utils",
"lazy_static",
"num_cpus",
@@ -283,18 +311,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.16"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
[[package]]
name = "scopeguard"
@@ -302,21 +321,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "strsim"
version = "0.8.0"
@@ -334,27 +338,33 @@ dependencies = [
[[package]]
name = "unicode-width"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "vec_map"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wham"
version = "0.9.6"
version = "1.1.2"
dependencies = [
"GSL",
"assert_approx_eq",
"clap",
"error-chain",
"rand",
@@ -363,9 +373,9 @@ dependencies = [
[[package]]
name = "winapi"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",

View File

@@ -1,20 +1,26 @@
[package]
name = "wham"
version = "0.9.6"
version = "1.1.2"
authors = ["Daniel Bauer <bauer@cbs.tu-darmstadt.de>"]
description = "An implementation of the weighted histogram analysis method"
license = "GPL-3.0"
repository = "https://github.com/danijoo/WHAM"
readme = "README.md"
categories = ["science", "command-line-utilities", "algorithms"]
keywords = ["math", "statistics", "histogram", "bioinformatics", "molecular-dynamics"]
exclude = [
"example/*"
]
[dependencies]
clap = {version="2.32.0", features=['yaml']}
error-chain = "0.12.0"
rand = "0.5.5"
GSL = "1.0.0"
rand = "0.7.*"
rayon = "1.0.3"
[profile.release]
opt-level = 2
[dev-dependencies]
assert_approx_eq = "1.1.0"
[profile.release]
opt-level = 3
[features]
default = ["GSL/v2"]

103
README.md
View File

@@ -12,19 +12,14 @@ from umbrella sampling simulations. For more details on the method, I suggest *R
Features
---
- Fast, especially for small systems
- Multithreaded
- Multidimensional
- Error analysis
- Multithreaded (automatically runs on all available cores)
- Multidimensional (any number of collective variables are possible)
- Autocorrelation to remove correlated samples
- Error analysis via bootstrapping
- Unit tested
Installation
---
WHAM requires the GSL library to be installed:
```bash
# on debian/ubuntu:
sudo apt-get install libgsl0-dev
```
Installation from source via cargo:
```bash
# cargo installation
@@ -38,7 +33,71 @@ Usage
wham has a convenient command line interface. You can see all options with
```wham -h```:
To run the two dimensional example (simulation of dialanine phi and psi angle):
```
wham 1.1.0
D. Bauer <bauer@cbs.tu-darmstadt.de>
wham is a fast implementation of the weighted histogram analysis method (WHAM) written in Rust. It currently supports
potential of mean force (PMF) calculations in multiple dimensions at constant temperature.
Metadata file format:
/path/to/timeseries_file1 x_1 x_2 x_N fc_1 fc_2 fc_N
/path/to/timeseries_file2 x_1 x_2 x_N fc_1 fc_2 fc_N
/path/to/timeseries_file3 x_1 x_2 x_N fc_1 fc_2 fc_N
The first column is a path to a timeseries file _relative_ to the metadata file (see below). This is followed by the
position of the umbrella potential x in N dimensions and the force constant fc in each dimension. Lines starting with a
# are treated as comments and will not be parsed.
Timeseries file format:
time x_1 x_2 x_N
time x_1 x_2 x_N
time x_1 x_2 x_N
The first column will be ignored and is followed by N reaction coordinates x.
Shipped under the GPLv3 license.
USAGE:
wham [FLAGS] [OPTIONS] --bins <BINS> --max <HIST_MAX> --file <METADATA> --min <HIST_MIN> --temperature <temperature>
FLAGS:
-c, --cyclic For periodic reaction coordinates. If this is set, the first and last coordinate bin in each
dimension are treated as neighbors for the bias calculation.
-h, --help Prints help information
-g, --uncorr Estimates statistical inefficiency of each timeseries via autocorrelation and removes correlated
samples (default is off).
-V, --version Prints version information
-v, --verbose Enables verbose output.
OPTIONS:
-b, --bins <BINS> Number of histogram bins (comma separated).
--bt <bootstrap> Number of bayesian bootstrapping runs for error analysis by assigning random
weights (defaults to 0).
--seed <bootstrap_seed> Random seed for bootstrapping runs.
--convdt <convdt> Performs WHAM for slices with the given delta in time and returns an output file
for each slice. THis is useful to check the result for convergence. Example: with
--convdt 100 and a timeseries ranging from 0-300, free energy surfaces for slices
0-100, 0-200 and 0-300 will be given returned.
--end <end> Skip rows in timeseries with an index larger than this value (defaults to 1e+20)
-i, --iterations <ITERATIONS> Stop WHAM after this many iterations without convergence (defaults to 100,000).
--max <HIST_MAX> Histogram maxima (comma separated). Also accepts "pi".
-f, --file <METADATA> Path to the metadata file.
--min <HIST_MIN> Histogram minima (comma separated for multiple dimensions). Also accepts "pi".
-o, --output <output> Free energy output file (defaults to wham.out).
--start <start> Skip rows in timeseries with an index smaller than this value (defaults to 0)
-T, --temperature <temperature> WHAM temperature in Kelvin.
-t, --tolerance <TOLERANCE> Abortion criteria for WHAM calculation. WHAM stops if abs(F_new - F_old) <
tolerance (defaults to 0.000001).
```
Examples
---
The example folder contains input and output files for two simple test systems:
- 1d_cyclic: Phi torsion angle of dialanine in vaccum
- 2d_cyclic: Phi and psi torsion angles of the same system
The command below will run the two dimensional example (simulation of dialanine phi and psi angle) and calculate the free energy based on the two collective variables
in the range of -3.14 to 3.14, with 100 bins in each dimension and periodic collective variables:
```bash
wham --max 3.14,3.14 --min -3.14,-3.14 -T 300 --bins 100,100 --cyclic -f example/2d/metadata.dat
> Supplied WHAM options: Metadata=example/2d/metadata.dat, hist_min=[-3.14, -3.14], hist_max=[3.14, 3.14], bins=[100, 100] verbose=false, tolerance=0.000001, iterations=100000, temperature=300, cyclic=true
@@ -55,7 +114,6 @@ wham --max 3.14,3.14 --min -3.14,-3.14 -T 300 --bins 100,100 --cyclic -f example
```
After convergence, final bias offsets (F) and the free energy will be dumped to stdout and the output file is written.
The output file contains the free energy and probability for each bin. Probabilities are normalized to sum to P=1.0 and
the smallest free energy is set to 0 (with other free energies based on that).
```
@@ -75,27 +133,30 @@ the smallest free energy is set to 0 (with other free energies based on that).
Error analysis
---
WHAM can perform error analysis using the bayesian bootstrapping method. Every simulation window is assumed to be an
individual set of data point. By calculating probabilities N times with randomly assigned weights for each window,
individual set of data points. By calculating probabilities N times with randomly assigned weights for each window,
one can estimate the error as standard deviation between the N bootstrapping runs. For more details see
*Van der Spoel, D. et al. (2010). g_wham—A Free Weighted Histogram Analysis Implementation Including Robust Error and
Autocorrelation Estimates, JCTC, 6(12), 3713-3720*.
To perform bayesian bootstrapping in WHAM, use the ```-bt <RUNS>``` flag to perform <RUNS> individual bootstrapping
runs. The error estimates of bin probabilities and free energy will be given as separate column (+/-) in the output file.
If no error analysis is performed, these columns are set to 0.0.
runs. The error estimates of bin probabilities and free energy will be given as standard error (SE) in a
separate column (+/-) in the output file. If no error analysis is performed, these columns are set to 0.0.
Examples
Autocorrelation analysis
---
The example folder contains input and output files for two simple test systems:
With the ```--uncorr``` flag, WHAM calculates the autocorrelation time ```tau``` for all timeseries and all collective
variables. Timeseries are then filtered based on their highest autocorrelation time to remove correlated samples from
the dataset. This reduces the number of data points but can improve the accuracy of the result.
- 1d_cyclic: Phi torsion angle of dialanine in vaccum
- 2d_cyclic: Phi and psi torsion angles of the same system
For filtering, the statistical inefficiency `g` is calculated: ```g = 1 + 2*tau```, and only every `g`th element of the
timeseries is used for unbiasing. A more detailed description of the method can be found in
*Chodera, J.D. et al. (2007). Use of the weighted histogram analysis method for the analysis of simulated and parallel
tempering simulations, JCTC 3(1):26-41*
TODO
---
- better error estimation
- Autocorrelation
- Option to output histograms
- Replica exchange
License & Citing
@@ -103,7 +164,7 @@ License & Citing
WHAM is licensed under the GPL-3.0 license. Please read the LICENSE file in this
repository for more information.
There's no publication for this WHAM implementation. However, there is a citeabe DOI. If you use this software for your work, please consider citing it: *Bauer, D, WHAM - An efficient weighted histogram analysis implementation written in Rust, Zenodo. https://doi.org/10.5281/zenodo.1488597*
There's no publication for this WHAM implementation. However, there is a citeabe DOI. If you use this software for your work, please consider citing it: *Bauer, D., WHAM - An efficient weighted histogram analysis implementation written in Rust, Zenodo. https://doi.org/10.5281/zenodo.1488597*
Parts of this work, especially some perfomance optimizations and the I/O format, are inspired by the
implementation of A. Grossfield (*Grossfield, A, WHAM: the weighted histogram analysis method, http://membrane.urmc.rochester.edu/content/wham*).

View File

@@ -1,101 +1,101 @@
#coord1 Free Energy +/- Probability +/-
-3.110177 7.158102 0.476037 0.003494 0.000667
-3.047345 5.365727 0.470738 0.007168 0.001353
-2.984513 3.873190 0.433120 0.013039 0.002264
-2.921681 2.953162 0.441412 0.018855 0.003337
-2.858849 1.949554 0.443549 0.028195 0.005014
-2.796017 1.391747 0.407397 0.035261 0.005759
-2.733186 1.128270 0.380789 0.039189 0.005983
-2.670354 0.839970 0.370008 0.043991 0.006526
-2.607522 0.624769 0.381081 0.047955 0.007326
-2.544690 0.663757 0.389082 0.047211 0.007364
-2.481858 1.051932 0.379999 0.040407 0.006156
-2.419026 1.463048 0.394914 0.034267 0.005425
-2.356194 1.990616 0.335141 0.027734 0.003726
-2.293363 2.190692 0.196497 0.025597 0.002016
-2.230531 2.553036 0.177045 0.022136 0.001571
-2.167699 2.572522 0.208798 0.021964 0.001839
-2.104867 2.472360 0.168929 0.022863 0.001548
-2.042035 2.517562 0.166774 0.022453 0.001501
-1.979203 2.469778 0.182308 0.022887 0.001673
-1.916372 2.223125 0.405294 0.025266 0.004105
-1.853540 2.080157 0.340376 0.026756 0.003651
-1.790708 1.793841 0.394974 0.030011 0.004752
-1.727876 1.458784 0.335956 0.034326 0.004623
-1.665044 0.914441 0.336724 0.042697 0.005764
-1.602212 0.303813 0.320695 0.054540 0.007012
-1.539380 0.268335 0.330495 0.055321 0.007330
-1.476549 0.000000 0.338143 0.061604 0.008351
-1.413717 0.537061 0.369220 0.049671 0.007352
-1.350885 1.439940 0.381006 0.034586 0.005283
-1.288053 2.391838 0.407306 0.023614 0.003856
-1.225221 3.779470 0.413629 0.013538 0.002245
-1.162389 5.685555 0.432485 0.006305 0.001093
-1.099557 7.661896 0.446994 0.002855 0.000512
-1.036726 9.946594 0.489990 0.001142 0.000224
-0.973894 12.359408 0.544847 0.000434 0.000095
-0.911062 14.954655 0.622373 0.000153 0.000038
-0.848230 17.742242 0.747233 0.000050 0.000015
-0.785398 20.555785 0.921773 0.000016 0.000006
-0.722566 22.811160 1.179343 0.000007 0.000003
-0.659734 25.178094 1.484376 0.000003 0.000002
-0.596903 26.442288 1.565063 0.000002 0.000001
-0.534071 27.897565 1.418232 0.000001 0.000000
-0.471239 29.062473 1.246738 0.000001 0.000000
-0.408407 30.384521 1.231662 0.000000 0.000000
-0.345575 31.638454 1.209295 0.000000 0.000000
-0.282743 32.817727 1.291637 0.000000 0.000000
-0.219911 33.770369 1.323362 0.000000 0.000000
-0.157080 34.505503 1.396454 0.000000 0.000000
-0.094248 35.431659 1.662548 0.000000 0.000000
-0.031416 35.615810 1.766523 0.000000 0.000000
0.031416 35.561946 1.799603 0.000000 0.000000
0.094248 35.382089 1.374764 0.000000 0.000000
0.157080 34.934827 1.659669 0.000000 0.000000
0.219911 33.673460 1.317449 0.000000 0.000000
0.282743 32.731563 1.191975 0.000000 0.000000
0.345575 31.261855 1.199834 0.000000 0.000000
0.408407 29.717377 1.339899 0.000000 0.000000
0.471239 28.076620 1.303458 0.000001 0.000000
0.534071 26.481097 1.281459 0.000002 0.000001
0.596903 24.487358 1.251139 0.000003 0.000002
0.659734 22.344251 1.233967 0.000008 0.000004
0.722566 20.241543 1.251834 0.000018 0.000009
0.785398 18.341869 1.241684 0.000039 0.000020
0.848230 16.261582 1.281159 0.000091 0.000047
0.911062 14.301801 1.294427 0.000199 0.000103
0.973894 12.603788 1.270069 0.000394 0.000200
1.036726 11.249601 1.273360 0.000678 0.000346
1.099557 10.087886 1.280795 0.001079 0.000554
1.162389 9.443303 1.266983 0.001398 0.000710
1.225221 9.152799 1.255009 0.001570 0.000790
1.288053 9.331937 1.256822 0.001462 0.000736
1.350885 9.905546 1.251237 0.001161 0.000583
1.413717 11.042050 1.263816 0.000736 0.000373
1.476549 12.598167 1.275325 0.000395 0.000202
1.539380 14.520167 1.280735 0.000183 0.000094
1.602212 16.569783 1.295384 0.000080 0.000042
1.665044 18.687390 1.309518 0.000034 0.000018
1.727876 20.775408 1.356414 0.000015 0.000008
1.790708 22.905200 1.308568 0.000006 0.000003
1.853540 24.643852 1.425870 0.000003 0.000002
1.916372 26.301740 1.362138 0.000002 0.000001
1.979203 27.372071 1.391716 0.000001 0.000001
2.042035 28.697726 1.504676 0.000001 0.000000
2.104867 29.417901 1.369019 0.000000 0.000000
2.167699 30.008351 1.409081 0.000000 0.000000
2.230531 30.406016 1.240128 0.000000 0.000000
2.293363 30.171275 1.179444 0.000000 0.000000
2.356194 29.884646 1.219365 0.000000 0.000000
2.419026 29.428153 1.177281 0.000000 0.000000
2.481858 28.546982 1.351057 0.000001 0.000000
2.544690 27.757520 1.238695 0.000001 0.000000
2.607522 26.505787 1.308430 0.000001 0.000001
2.670354 24.491866 1.039660 0.000003 0.000001
2.733186 22.320664 0.927893 0.000008 0.000003
2.796017 20.052723 0.894483 0.000020 0.000007
2.858849 17.655650 0.893580 0.000052 0.000019
2.921681 15.471590 0.930897 0.000125 0.000047
2.984513 13.138167 0.837026 0.000318 0.000107
3.047345 11.092386 0.674785 0.000722 0.000195
3.110177 9.065722 0.521230 0.001626 0.000340
-3.110177 7.158102 0.071438 0.003494 0.000068
-3.047345 5.365727 0.069449 0.007168 0.000135
-2.984513 3.873190 0.067495 0.013039 0.000232
-2.921681 2.953162 0.067589 0.018855 0.000343
-2.858849 1.949554 0.064640 0.028195 0.000480
-2.796017 1.391747 0.063570 0.035261 0.000584
-2.733186 1.128270 0.061710 0.039189 0.000620
-2.670354 0.839970 0.060445 0.043991 0.000667
-2.607522 0.624769 0.060762 0.047955 0.000739
-2.544690 0.663757 0.060786 0.047211 0.000731
-2.481858 1.051932 0.059774 0.040407 0.000617
-2.419026 1.463048 0.060422 0.034267 0.000527
-2.356194 1.990616 0.055242 0.027734 0.000360
-2.293363 2.190692 0.044458 0.025597 0.000210
-2.230531 2.553036 0.042144 0.022136 0.000159
-2.167699 2.572522 0.043313 0.021964 0.000165
-2.104867 2.472360 0.039799 0.022863 0.000157
-2.042035 2.517562 0.036792 0.022453 0.000203
-1.979203 2.469778 0.036324 0.022887 0.000237
-1.916372 2.223125 0.049496 0.025266 0.000565
-1.853540 2.080157 0.039205 0.026756 0.000453
-1.790708 1.793841 0.041488 0.030011 0.000558
-1.727876 1.458784 0.042362 0.034326 0.000565
-1.665044 0.914441 0.037561 0.042697 0.000682
-1.602212 0.303813 0.028701 0.054540 0.000761
-1.539380 0.268335 0.025765 0.055321 0.000771
-1.476549 0.000000 0.023371 0.061604 0.000866
-1.413717 0.537061 0.026370 0.049671 0.000715
-1.350885 1.439940 0.025620 0.034586 0.000547
-1.288053 2.391838 0.027653 0.023614 0.000393
-1.225221 3.779470 0.027093 0.013538 0.000229
-1.162389 5.685555 0.027762 0.006305 0.000111
-1.099557 7.661896 0.028996 0.002855 0.000051
-1.036726 9.946594 0.032118 0.001142 0.000021
-0.973894 12.359408 0.042801 0.000434 0.000009
-0.911062 14.954655 0.052443 0.000153 0.000004
-0.848230 17.742242 0.063261 0.000050 0.000002
-0.785398 20.555785 0.068133 0.000016 0.000001
-0.722566 22.811160 0.075518 0.000007 0.000000
-0.659734 25.178094 0.086226 0.000003 0.000000
-0.596903 26.442288 0.091783 0.000002 0.000000
-0.534071 27.897565 0.093175 0.000001 0.000000
-0.471239 29.062473 0.096027 0.000001 0.000000
-0.408407 30.384521 0.096855 0.000000 0.000000
-0.345575 31.638454 0.099956 0.000000 0.000000
-0.282743 32.817727 0.107689 0.000000 0.000000
-0.219911 33.770369 0.108946 0.000000 0.000000
-0.157080 34.505503 0.112902 0.000000 0.000000
-0.094248 35.431659 0.120856 0.000000 0.000000
-0.031416 35.615810 0.122046 0.000000 0.000000
0.031416 35.561946 0.118225 0.000000 0.000000
0.094248 35.382089 0.108652 0.000000 0.000000
0.157080 34.934827 0.118384 0.000000 0.000000
0.219911 33.673460 0.119195 0.000000 0.000000
0.282743 32.731563 0.121592 0.000000 0.000000
0.345575 31.261855 0.129698 0.000000 0.000000
0.408407 29.717377 0.139789 0.000000 0.000000
0.471239 28.076620 0.137289 0.000001 0.000000
0.534071 26.481097 0.135712 0.000002 0.000000
0.596903 24.487358 0.135156 0.000003 0.000000
0.659734 22.344251 0.131027 0.000008 0.000000
0.722566 20.241543 0.133145 0.000018 0.000001
0.785398 18.341869 0.131952 0.000039 0.000002
0.848230 16.261582 0.134676 0.000091 0.000006
0.911062 14.301801 0.134417 0.000199 0.000013
0.973894 12.603788 0.131018 0.000394 0.000024
1.036726 11.249601 0.131276 0.000678 0.000043
1.099557 10.087886 0.132498 0.001079 0.000070
1.162389 9.443303 0.132990 0.001398 0.000092
1.225221 9.152799 0.132250 0.001570 0.000104
1.288053 9.331937 0.133178 0.001462 0.000099
1.350885 9.905546 0.133357 0.001161 0.000078
1.413717 11.042050 0.133807 0.000736 0.000051
1.476549 12.598167 0.132722 0.000395 0.000027
1.539380 14.520167 0.131816 0.000183 0.000012
1.602212 16.569783 0.131773 0.000080 0.000005
1.665044 18.687390 0.132601 0.000034 0.000002
1.727876 20.775408 0.133867 0.000015 0.000001
1.790708 22.905200 0.129265 0.000006 0.000000
1.853540 24.643852 0.128894 0.000003 0.000000
1.916372 26.301740 0.130266 0.000002 0.000000
1.979203 27.372071 0.128620 0.000001 0.000000
2.042035 28.697726 0.133263 0.000001 0.000000
2.104867 29.417901 0.133513 0.000000 0.000000
2.167699 30.008351 0.130925 0.000000 0.000000
2.230531 30.406016 0.124398 0.000000 0.000000
2.293363 30.171275 0.122493 0.000000 0.000000
2.356194 29.884646 0.130278 0.000000 0.000000
2.419026 29.428153 0.130844 0.000000 0.000000
2.481858 28.546982 0.148114 0.000001 0.000000
2.544690 27.757520 0.133045 0.000001 0.000000
2.607522 26.505787 0.134364 0.000001 0.000000
2.670354 24.491866 0.115061 0.000003 0.000000
2.733186 22.320664 0.110036 0.000008 0.000000
2.796017 20.052723 0.107894 0.000020 0.000001
2.858849 17.655650 0.105964 0.000052 0.000002
2.921681 15.471590 0.107005 0.000125 0.000005
2.984513 13.138167 0.099378 0.000318 0.000012
3.047345 11.092386 0.087770 0.000722 0.000022
3.110177 9.065722 0.077092 0.001626 0.000038

View File

@@ -0,0 +1,101 @@
#coord1 Free Energy +/- Probability +/-
-3.110177 7.531315 0.000000 0.003080 0.000000
-3.047345 5.690157 0.000000 0.006443 0.000000
-2.984513 4.243063 0.000000 0.011509 0.000000
-2.921681 3.334686 0.000000 0.016564 0.000000
-2.858849 2.277349 0.000000 0.025309 0.000000
-2.796017 1.723296 0.000000 0.031604 0.000000
-2.733186 1.246264 0.000000 0.038265 0.000000
-2.670354 1.099867 0.000000 0.040578 0.000000
-2.607522 0.771910 0.000000 0.046279 0.000000
-2.544690 0.770616 0.000000 0.046303 0.000000
-2.481858 1.265507 0.000000 0.037971 0.000000
-2.419026 1.562335 0.000000 0.033711 0.000000
-2.356194 1.891577 0.000000 0.029542 0.000000
-2.293363 2.227858 0.000000 0.025816 0.000000
-2.230531 2.488355 0.000000 0.023256 0.000000
-2.167699 2.502265 0.000000 0.023127 0.000000
-2.104867 2.358037 0.000000 0.024503 0.000000
-2.042035 2.278147 0.000000 0.025301 0.000000
-1.979203 2.974067 0.000000 0.019141 0.000000
-1.916372 2.696600 0.000000 0.021393 0.000000
-1.853540 2.361827 0.000000 0.024466 0.000000
-1.790708 1.516746 0.000000 0.034332 0.000000
-1.727876 1.526829 0.000000 0.034194 0.000000
-1.665044 0.884114 0.000000 0.044244 0.000000
-1.602212 0.323912 0.000000 0.055385 0.000000
-1.539380 0.197985 0.000000 0.058253 0.000000
-1.476549 0.000000 0.000000 0.063065 0.000000
-1.413717 0.458247 0.000000 0.052481 0.000000
-1.350885 1.389410 0.000000 0.036131 0.000000
-1.288053 2.386522 0.000000 0.024225 0.000000
-1.225221 3.743253 0.000000 0.014062 0.000000
-1.162389 5.566654 0.000000 0.006770 0.000000
-1.099557 7.822800 0.000000 0.002740 0.000000
-1.036726 10.128719 0.000000 0.001087 0.000000
-0.973894 12.199246 0.000000 0.000474 0.000000
-0.911062 14.488129 0.000000 0.000189 0.000000
-0.848230 16.902310 0.000000 0.000072 0.000000
-0.785398 18.910200 0.000000 0.000032 0.000000
-0.722566 21.241681 0.000000 0.000013 0.000000
-0.659734 22.706373 0.000000 0.000007 0.000000
-0.596903 24.531129 0.000000 0.000003 0.000000
-0.534071 25.936227 0.000000 0.000002 0.000000
-0.471239 27.000262 0.000000 0.000001 0.000000
-0.408407 28.673293 0.000000 0.000001 0.000000
-0.345575 29.335203 0.000000 0.000000 0.000000
-0.282743 30.841118 0.000000 0.000000 0.000000
-0.219911 31.983859 0.000000 0.000000 0.000000
-0.157080 32.144015 0.000000 0.000000 0.000000
-0.094248 33.885395 0.000000 0.000000 0.000000
-0.031416 33.783105 0.000000 0.000000 0.000000
0.031416 34.243727 0.000000 0.000000 0.000000
0.094248 33.975567 0.000000 0.000000 0.000000
0.157080 32.994787 0.000000 0.000000 0.000000
0.219911 32.607398 0.000000 0.000000 0.000000
0.282743 31.401902 0.000000 0.000000 0.000000
0.345575 29.911670 0.000000 0.000000 0.000000
0.408407 28.603574 0.000000 0.000001 0.000000
0.471239 26.925443 0.000000 0.000001 0.000000
0.534071 25.298070 0.000000 0.000002 0.000000
0.596903 23.638560 0.000000 0.000005 0.000000
0.659734 21.156231 0.000000 0.000013 0.000000
0.722566 19.126480 0.000000 0.000029 0.000000
0.785398 17.351953 0.000000 0.000060 0.000000
0.848230 15.135525 0.000000 0.000146 0.000000
0.911062 13.188112 0.000000 0.000319 0.000000
0.973894 11.536983 0.000000 0.000618 0.000000
1.036726 10.158328 0.000000 0.001074 0.000000
1.099557 9.109036 0.000000 0.001636 0.000000
1.162389 8.282343 0.000000 0.002279 0.000000
1.225221 8.022102 0.000000 0.002530 0.000000
1.288053 8.162415 0.000000 0.002391 0.000000
1.350885 8.600135 0.000000 0.002006 0.000000
1.413717 9.837348 0.000000 0.001222 0.000000
1.476549 11.363156 0.000000 0.000663 0.000000
1.539380 13.077849 0.000000 0.000333 0.000000
1.602212 15.353594 0.000000 0.000134 0.000000
1.665044 17.565051 0.000000 0.000055 0.000000
1.727876 19.710884 0.000000 0.000023 0.000000
1.790708 21.721260 0.000000 0.000010 0.000000
1.853540 23.567649 0.000000 0.000005 0.000000
1.916372 25.008817 0.000000 0.000003 0.000000
1.979203 26.405367 0.000000 0.000002 0.000000
2.042035 28.070821 0.000000 0.000001 0.000000
2.104867 28.877213 0.000000 0.000001 0.000000
2.167699 29.378146 0.000000 0.000000 0.000000
2.230531 31.093267 0.000000 0.000000 0.000000
2.293363 30.704994 0.000000 0.000000 0.000000
2.356194 30.563093 0.000000 0.000000 0.000000
2.419026 31.215952 0.000000 0.000000 0.000000
2.481858 30.331416 0.000000 0.000000 0.000000
2.544690 29.005123 0.000000 0.000001 0.000000
2.607522 27.674618 0.000000 0.000001 0.000000
2.670354 24.911788 0.000000 0.000003 0.000000
2.733186 22.746417 0.000000 0.000007 0.000000
2.796017 20.608288 0.000000 0.000016 0.000000
2.858849 18.018280 0.000000 0.000046 0.000000
2.921681 15.949215 0.000000 0.000105 0.000000
2.984513 13.617809 0.000000 0.000268 0.000000
3.047345 11.432193 0.000000 0.000645 0.000000
3.110177 9.461494 0.000000 0.001420 0.000000

View File

@@ -1,6 +1,6 @@
name: wham
version: "0.9.0"
author: D. Bauer <bauer@bio.tu-darmstadt.de>
version: "1.1.2"
author: D. Bauer <bauer@cbs.tu-darmstadt.de>
about: |
wham is a fast implementation of the weighted histogram analysis method (WHAM) written in Rust. It currently supports potential of mean force (PMF) calculations in multiple dimensions at constant temperature.
@@ -101,3 +101,19 @@ args:
help: Skip rows in timeseries with an index larger than this value (defaults to 1e+20)
takes_value: true
required: false
- uncorr:
short: g
long: uncorr
help: Estimates statistical inefficiency of each timeseries via autocorrelation and removes correlated samples (default is off).
takes_value: false
required: false
- convdt:
long: convdt
help: "Performs WHAM for slices with the given delta in time and returns an output file for each slice. THis is useful to check the result for convergence. Example: with --convdt 100 and a timeseries ranging from 0-300, free energy surfaces for slices 0-100, 0-200 and 0-300 will be given returned."
takes_value: true
required: false
- ignore_empty:
long: ignore_empty
help: If this is set, do not fail if a histogram is empty.
takes_value: false
required: false

View File

@@ -0,0 +1,78 @@
use super::statistics;
// calculates the statistical inefficiency g of the given timeseries
// the quantity g can be thought of: N/g is the number of uncorrelated
// configurations in the timeseries, where samples are separated by
// the a multiple of g
// For details, see "Chodera et al. (2007). Use of a Weighted Histogram Analysis
// Method for the Analysis of Simulated and Parallel Tempering Simulations, JCTC"
pub fn statistical_ineff(timeseries: &[f64]) -> f64 {
let n = timeseries.len();
let mean = statistics::mean(timeseries);
let d_mean = timeseries.iter().map(|x| x-mean).collect::<Vec<f64>>();
let cov = statistics::autocov(timeseries);
let mut g = 1.0;
for t in 1..(n-1) {
let tmp = d_mean[0..n-t].iter().zip(d_mean[t..n].iter()).map(|(x,y)| x*y);
let c = tmp.map(|x| x+x).sum::<f64>() / (2.0 * (n as f64 - t as f64)*cov);
if c <= 0.0 {
break;
}
g = g + (2.0*c*(1.0-t as f64/n as f64))
}
if g < 1.0 {
1.0
} else {
g
}
}
// The autocorrelation time of a timeseries can be deduced from the
// `statistical_ineff` by (g-1)/2.0
pub fn autocorrelation_time(g: f64) -> f64 {
(g - 1.0) / 2.0
}
#[cfg(test)]
mod tests {
use std::io::{BufRead, BufReader};
use std::fs::File;
fn read_timeseries(filename: &str) -> Vec<f64> {
let mut timeseries: Vec<f64> = Vec::new();
let file = File::open(filename).unwrap();
let reader = BufReader::new(&file);
for line in reader.lines() {
let val = line.unwrap().split_whitespace()
.collect::<Vec<&str>>()[1].parse::<f64>().unwrap();
timeseries.push(val);
}
timeseries
}
#[test]
fn statistical_ineff() {
let timeseries = read_timeseries("example/1d_cyclic/COLVAR-2.5.xvg");
let g = super::statistical_ineff(&timeseries);
println!("{:?}", g);
assert!((g - 3.859).abs() < 0.001);
// a "random" timeseries with g < 1.0
let timeseries = [1_f64, 4_f64, 921_f64, 121213_f64, 23192_f64,
8913_f64, 1232_f64, 2_f64, 151_f64, 123091_f64];
let g = super::statistical_ineff(&timeseries);
assert_approx_eq!(g, 1.0);
}
#[test]
fn autocorrelation_time() {
let timeseries = read_timeseries("example/1d_cyclic/COLVAR-2.5.xvg");
let g = super::statistical_ineff(&timeseries);
let tau = super::autocorrelation_time(g);
println!("{:?}", tau);
assert!((tau - 1.430).abs() < 0.001)
}
}

View File

@@ -1,8 +1,8 @@
use rand::{SeedableRng, StdRng, Rng};
use rand::prelude::*;
use super::histogram::{Dataset};
use super::perform_wham;
use super::Config;
use rgsl::statistics;
use super::{Config,calc_free_energy};
use super::statistics;
// returns a set of num_windows continious weights by
// a) generate num_windows-1 random variables and sort them
@@ -20,7 +20,7 @@ fn generate_random_weights(num_windows: usize, rng: &mut StdRng) -> Vec<f64> {
for i in 0..num_windows {
weights[i] = rnds[i+1] - rnds[i]
}
return weights
weights
}
// Generate a random weighted dataset from the given dataset by changing the weights
@@ -32,7 +32,7 @@ fn generate_random_weighted_dataset(ds: Dataset, rng: &mut StdRng) -> Dataset {
// Perform bootstrap error analysis. This runs the WHAM analysis num_runs times on random weighted
// datasets. The standard deviation is calculated on the bootstrapped probabilities of each bin. The
// standard deviation of the free eneergy is then deduced by error propagation (A_std = kT*1/P*P_std)
pub fn run_bootstrap(cfg: &Config, ds: Dataset, P: &[f64], num_runs: usize) -> (Vec<f64>,Vec<f64>) {
pub fn run_bootstrap(cfg: &Config, ds: Dataset, num_runs: usize) -> (Vec<f64>,Vec<f64>) {
// seed the rng
let mut rng: StdRng = SeedableRng::seed_from_u64(cfg.bootstrap_seed);
@@ -43,19 +43,30 @@ pub fn run_bootstrap(cfg: &Config, ds: Dataset, P: &[f64], num_runs: usize) -> (
perform_wham(cfg, &rnd_weighted_dataset).unwrap().0
}).collect();
// Evaulate standard deviation of P per bin
let mut P_std = vec![0.0; ds.num_bins];
// Standard error (SE) of P per bin
// SE = SD/sqrt(n)
let mut P_se = vec![0.0; ds.num_bins];
for bin in 0..ds.num_bins {
let Ps = bootstrapped_Ps.iter().map(|window| window[bin]).collect::<Vec<f64>>();
P_std[bin] = statistics::sd(&Ps, 1, num_runs);
P_se[bin] = statistics::sd(&Ps)/(num_runs as f64).sqrt();
}
// A_std by error propagation
let A_std = P_std.iter().zip(P.iter()).map(|(std,P)| ds.kT*1.0/P*std).collect();
(P_std, A_std)
// SE of A
let bootstrapped_As: Vec<Vec<f64>> = (0..num_runs).map(|x| {
let run_Ps = &bootstrapped_Ps[x];
calc_free_energy(&ds, run_Ps)
}).collect();
let mut A_se = vec![0.0; ds.num_bins];
for bin in 0..ds.num_bins {
let As = bootstrapped_As.iter().map(|window| window[bin]).collect::<Vec<f64>>();
A_se[bin] = statistics::sd(&As)/(num_runs as f64).sqrt();
}
(P_se, A_se)
}
#[cfg(tests)]
#[cfg(test)]
mod tests {
use super::*;
use super::super::k_B;
@@ -88,8 +99,9 @@ mod tests {
#[test]
fn random_weights() {
let mut rng = StdRng::from_entropy();
let num_windows = 5;
let weights = generate_random_weights(num_windows);
let weights = generate_random_weights(num_windows, &mut rng);
assert_eq!(num_windows, weights.len());
for w in weights {
assert!(0.0 < w && w < 1.0);
@@ -98,8 +110,9 @@ mod tests {
#[test]
fn random_weighted_dataset() {
let mut rng = StdRng::from_entropy();
let ds = build_hist_set();
let rnd_weights_ds = generate_random_weighted_dataset(ds);
let rnd_weights_ds = generate_random_weighted_dataset(ds, &mut rng);
println!("{:?}", rnd_weights_ds.weights);
for w in rnd_weights_ds.weights {
assert!(w > 0.0);

View File

@@ -61,7 +61,9 @@ pub struct Dataset {
impl Dataset {
pub fn new(num_bins: usize, dimens_lengths: Vec<usize>, bin_width: Vec<f64>, hist_min: Vec<f64>, hist_max: Vec<f64>, bias_pos: Vec<f64>, bias_fc: Vec<f64>, kT: f64, histograms: Vec<Histogram>, cyclic: bool) -> Dataset {
pub fn new(num_bins: usize, dimens_lengths: Vec<usize>, bin_width: Vec<f64>,
hist_min: Vec<f64>, hist_max: Vec<f64>, bias_pos: Vec<f64>,
bias_fc: Vec<f64>, kT: f64, histograms: Vec<Histogram>, cyclic: bool) -> Dataset {
let num_windows = histograms.len();
let bias: Vec<f64> = vec![0.0; num_bins*num_windows];
let weights = vec![1.0; num_windows];
@@ -92,7 +94,7 @@ impl Dataset {
pub fn new_weighted(ds: Dataset, weights: Vec<f64>) -> Dataset {
Dataset {
weights: weights,
weights,
..ds
}
}
@@ -105,9 +107,9 @@ impl Dataset {
let mut tmp = bin;
let mut idx = vec![0; lengths.len()];
for dimen in (1..lengths.len()).rev() {
let denom = lengths.iter().take(dimen).fold(1, |s,&x| s*x);
let denom: usize = lengths.iter().take(dimen).product();
idx[dimen] = tmp / denom;
tmp = tmp % denom;
tmp %= denom;
}
idx[0] = tmp;
idx
@@ -151,8 +153,7 @@ impl Dataset {
// store exp(U/kT) for better performance
bias_sum += 0.5 * bias_fc[i] * dist * dist
}
let bias_sum = (-bias_sum/self.kT).exp();
bias_sum
(-bias_sum/self.kT).exp()
}
}
@@ -205,13 +206,13 @@ mod tests {
let ds = build_hist_set(); // k = 10
// 3th element -> x=3.5, x0=3.5
assert_delta!(0.134722337796, ds.calc_bias(3, 0), 0.00000001);
assert_delta!(0.134_722_337_796, ds.calc_bias(3, 0), 0.000_000_01);
// 8th element -> x=8.5, x0=3.5
assert_delta!(1.0, ds.calc_bias(4,0), 0.00000001);
assert_delta!(1.0, ds.calc_bias(4,0), 0.000_000_01);
// 1st element -> x=0.5, x0=3.5. non-cyclic!
assert_delta!(0.0, ds.calc_bias(0,0), 0.0000001);
assert_delta!(0.0, ds.calc_bias(0,0), 0.000_000_1);
}
#[test]
@@ -220,28 +221,28 @@ mod tests {
ds.cyclic = true;
// 7th element -> x=3.5, x0=3.5
assert_delta!(0.134722337796, ds.calc_bias(3, 0), 0.00000001);
assert_delta!(0.134_722_337_796, ds.calc_bias(3, 0), 0.000_000_01);
// 8th element -> x=4.5, x0=3.5
assert_delta!(1.0, ds.calc_bias(4, 0), 0.00000001);
assert_delta!(1.0, ds.calc_bias(4, 0), 0.000_000_01);
// 1th element -> x=0.5, x0=3.5
// cyclic flag makes bin 0 neighboring bin 9, so the distance is actually 2
assert_delta!(0.0000000000000117769, ds.calc_bias(0, 0), 0.00000001);
assert_delta!(0.000_000_000_000_011_776_9, ds.calc_bias(0, 0), 0.000_000_01);
// 2nd element -> x=1.5, x0=3.5
assert_delta!(0.00000001, ds.calc_bias(1, 0), 0.00000001);
assert_delta!(0.000_000_01, ds.calc_bias(1, 0), 0.000_000_01);
}
#[test]
fn get_x_for_bin() {
let ds = build_hist_set();
let expected: Vec<f64> = vec![0,1,2,3,4,5,6,7,8].iter()
.map(|x| *x as f64 + 0.5).collect();
for i in 0..9 {
assert_eq!(expected[i], ds.get_coords_for_bin(i)[0]);
}
.map(|x| *x as f64 + 0.5).collect();
expected.iter().enumerate().for_each(|(i, exp)| {
assert_approx_eq!(exp, &ds.get_coords_for_bin(i)[0]);
})
}
#[test]
@@ -258,10 +259,10 @@ mod tests {
vec![build_hist(), build_hist()], // hists
false // cyclic
);
assert_delta!(2.0, ds.get_weighted_bin_count(0), 0.0000000001);
assert_delta!(2.0, ds.get_weighted_bin_count(1), 0.0000000001);
assert_delta!(6.0, ds.get_weighted_bin_count(2), 0.0000000001);
assert_delta!(10.0, ds.get_weighted_bin_count(3), 0.0000000001);
assert_delta!(24.0, ds.get_weighted_bin_count(4), 0.0000000001);
assert_delta!(2.0, ds.get_weighted_bin_count(0), 0.000_000_000_1);
assert_delta!(2.0, ds.get_weighted_bin_count(1), 0.000_000_000_1);
assert_delta!(6.0, ds.get_weighted_bin_count(2), 0.000_000_000_1);
assert_delta!(10.0, ds.get_weighted_bin_count(3), 0.000_000_000_1);
assert_delta!(24.0, ds.get_weighted_bin_count(4), 0.000_000_000_1);
}
}

436
src/io.rs
View File

@@ -1,6 +1,8 @@
use super::histogram::Dataset;
use super::histogram::Histogram;
use super::Config;
use super::correlation_analysis::{statistical_ineff, autocorrelation_time};
use std::fs::OpenOptions;
use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader,BufWriter};
@@ -25,28 +27,32 @@ pub fn vprintln(s: String, verbose: bool) {
}
// Read input data into a histogram set by iterating over input files
// given in the metadata file
pub fn read_data(cfg: &Config) -> Result<Dataset> {
// given in the metadata file. This generates at least one Dataset,
// or multiple Datasets if convdt is set in the config
pub fn read_data(cfg: &Config) -> Result<Vec<Dataset>> {
let mut bias_pos: Vec<f64> = Vec::new();
let mut bias_fc: Vec<f64> = Vec::new();
let mut histograms: Vec<Histogram> = Vec::new();
let mut histograms: Vec<Vec<Histogram>> = Vec::new();
let mut timeseries_lengths: Vec<usize> = Vec::new();
let mut paths = Vec::new();
let kT = cfg.temperature * k_B;
let bin_width: Vec<f64> = (0..cfg.dimens).map(|idx| {
(cfg.hist_max[idx] - cfg.hist_min[idx])/(cfg.num_bins[idx] as f64)
}).collect();
let num_bins = cfg.num_bins.iter().fold(1, |state, &bins| state*bins);
let num_bins: usize = cfg.num_bins.iter().product();
let dimens_length = cfg.num_bins.clone();
let f = File::open(&cfg.metadata_file).chain_err(|| "Failed to open metadata file")?;
let buf = BufReader::new(&f);
// read each metadata file line and parse it
for (line_num,l) in buf.lines().enumerate() {
let line = l.chain_err(|| "Failed to read line")?;
// skip comments and empty lines
if line.starts_with("#") || line.len() == 0 {
if line.starts_with('#') || line.is_empty() {
continue;
}
@@ -55,49 +61,176 @@ pub fn read_data(cfg: &Config) -> Result<Dataset> {
bail!(format!("Wrong number of columns in line {} of metadata file. Empty Line?", line_num+1));
}
// parse histogram data
let path = get_relative_path(&cfg.metadata_file, split[0]);
let h = read_window_file(&path, cfg)
.chain_err(|| format!("Failed to parse process data file {}", &path))?;
if h.num_points == 0 {
bail!(format!("No data points in histogram boundaries: {}", &path))
}
histograms.push(h);
vprintln(format!("{}, {} data points added.", &path,
histograms.last().unwrap().num_points), cfg.verbose);
// parse bias force constants and positions
for i in 1..cfg.dimens+1 {
let pos = split[i].parse()
for val in split.iter().skip(1).take(cfg.dimens) {
let pos = val.parse()
.chain_err(|| format!("Failed to read bias position in line {} of metadata file", line_num+1))?;
bias_pos.push(pos);
}
for i in (1+cfg.dimens)..(1+2*cfg.dimens) {
let fc = split[i].parse()
for val in split.iter().skip(1+cfg.dimens).take(cfg.dimens) {
let fc = val.parse()
.chain_err(|| format!("Failed to read bias fc in line {} of metadata file", line_num+1))?;
bias_fc.push(fc);
}
// parse histogram data
let path = get_relative_path(&cfg.metadata_file, split[0]);
paths.push(path.clone());
let (timeseries, timeseries_initial_lengths) = read_window_file(&path, cfg)
.chain_err(|| format!("Failed to read time series from {}", &path))?;
timeseries_lengths.push(timeseries_initial_lengths);
// for each timeseries, histograms are build for slices according to
// start..convdt, start..2*convdt, ...
histograms.push(Vec::new());
let h_idx = histograms.len()-1;
let convdt_stops = get_convdt_boundaries(&timeseries[0], &cfg);
for (idx, interval) in convdt_stops.iter().enumerate() {
// build histogram for slice start.._stop
let (start, stop) = interval;
let timeseries_mask: Vec<bool> = (0..timeseries[0].len()).map(|i| {
is_in_time_boundaries(timeseries[0][i], *start, *stop)
}).collect();
let hist = build_histogram_from_timeseries(&timeseries, &timeseries_mask, cfg);
histograms[h_idx].push(hist);
if (cfg.convdt == 0.00) || idx+1 == convdt_stops.len() {
vprintln(format!("{}, {} data points added.", &path,
histograms[h_idx].last().unwrap().num_points), cfg.verbose);
break
}
}
}
if histograms.len() > 0 {
Ok(Dataset::new(num_bins, dimens_length, bin_width, cfg.hist_min.clone(), cfg.hist_max.clone(), bias_pos, bias_fc, kT, histograms, cfg.cyclic))
// Datasets are created from histograms.
// Empty histograms result in an error when its the final dataset,
// and a warning otherwise.
let num_datasets: usize = histograms.iter().map(|h| h.len()).max().unwrap();
let dataset_boundaries: Vec<(f64, f64)> = (0..num_datasets).map(|idx| {
(cfg.start, cfg.start+(idx as f64 + 1.0)*cfg.convdt) }
).collect();
vprintln(format!("Generating {} datasets from histograms.", num_datasets), cfg.verbose);
let datasets: Vec<Dataset> = (0..num_datasets).map(|idx| {
let mut dataset_histograms: Vec<Histogram> = Vec::with_capacity(histograms.len());
for (hs, path) in histograms.iter().zip(&paths) {
if hs.len() > idx {
dataset_histograms.push(hs[idx].clone())
} else {
let warning = format!("No data points for interval {}-{} in histogram boundaries: {}.",
dataset_boundaries[idx].0, dataset_boundaries[idx].1 ,&path);
if !cfg.ignore_empty && idx+1 == num_datasets {
bail!(warning + " This is the final dataset.");
} else {
eprintln!("{}", warning);
}
}
}
Ok(Dataset::new(num_bins, dimens_length.clone(), bin_width.clone(),
cfg.hist_min.clone(), cfg.hist_max.clone(), bias_pos.clone(),
bias_fc.clone(), kT, dataset_histograms, cfg.cyclic))
}).collect::<Result<Vec<Dataset>>>().chain_err(|| "Failed to create datasets.")?;
if datasets.is_empty() {
bail!("No datasets created.")
} else if datasets[0].histograms.is_empty() {
bail!("Dataset has no associated data points.")
} else {
bail!("Histogram has no datapoints.")
if datasets.len() > 1 {
println!("Datasets:");
println!("Dataset\t\tTime interval\t\tWindows\t\tN_total");
for (idx, dataset) in datasets.iter().enumerate() {
let n: u32 = dataset.histograms.iter().map(|h| h.num_points).sum();
let mut stop = cfg.start+cfg.convdt*(idx+1) as f64;
if stop > cfg.end {
stop = cfg.end;
}
println!("{:?}\t\t{:?}-{:?}\t\t{:?}\t\t{:?}", idx+1, cfg.start, stop, dataset.histograms.len(), n);
}
}
let histograms = &datasets.last().unwrap().histograms;
if cfg.uncorr {
println!("Timeseries Correlation:");
println!("Window\t\tN\t\tN_uncorr\tN/N_uncorr");
for (idx, (n, h)) in timeseries_lengths.iter().zip(histograms.iter()).enumerate() {
println!("{:?}\t\t{:?}\t\t{:?}\t\t{:.2}",
idx+1, n, h.num_points, h.num_points as f64 / *n as f64);
}
let total_n = timeseries_lengths.iter().sum::<usize>() as f64;
let total_h = histograms.iter().map(|h| h.num_points).sum::<u32>() as f64;
println!("\t\t\t\t\tTotal:\t{:.2}", total_h/total_n);
}
Ok(datasets)
}
}
// builds a time boundaries for datasets from convdt, timeseries start and end
fn get_convdt_boundaries(timeseries: &[f64], cfg: &Config) -> Vec<(f64, f64)> {
let mut last_timestep = *timeseries.last().unwrap();
if last_timestep > cfg.end {
last_timestep = cfg.end;
}
let mut first_timestep = *timeseries.first().unwrap();
if first_timestep < cfg.start {
first_timestep = cfg.start;
}
if cfg.convdt == 0.0 {
vec![(0.0, last_timestep)]
} else {
let intervals: usize = ((last_timestep - first_timestep) / cfg.convdt).ceil() as usize;
(1..intervals+1).map(|i| {
i as f64 * cfg.convdt + first_timestep
}).map(|end| { (first_timestep, end) }).collect()
}
}
// build a histogram from a timeseries
// mask is used to filter the timeseries for selected frames
fn build_histogram_from_timeseries(timeseries: &[Vec<f64>], mask: &[bool],
cfg: &Config) -> Histogram {
// total number of bins is the product of all dimensions length
let total_bins = cfg.num_bins.iter().product();
// bin width for each dimension: (max-min)/bins
let bin_width: Vec<f64> = (0..cfg.dimens).map(|idx| {
(cfg.hist_max[idx] - cfg.hist_min[idx])/(cfg.num_bins[idx] as f64)
}).collect();
// build histogram for slice start..convdt_stop
let mut hist = vec![0.0; total_bins];
for i in (0..timeseries[0].len()).filter(|i| mask[*i]) {
let mut values: Vec<f64> = vec![f64::NAN; cfg.dimens+1];
for j in 0..values.len() {
values[j] = timeseries[j][i];
}
if is_in_hist_boundaries(&values[1..], cfg) {
let bin_indeces: Vec<usize> = (0..cfg.dimens).map(|dimen: usize| {
let val = values[dimen+1];
((val - cfg.hist_min[dimen]) / bin_width[dimen]) as usize
}).collect();
let index = flat_index(&bin_indeces, &cfg.num_bins);
hist[index] += 1.0;
}
}
let num_points: f64 = hist.iter().sum();
Histogram::new(num_points as u32, hist)
}
// transforms a multidimensional index into a one dimensional index
// indeces: multidimensional indeces
// lengths: length of the matrix in each dimension
// returns an index if the matrix is flattened to a one dimensional vector
// example for 3 dimensions N,M,O: idx = i_O + l_O*l_M*i_M + l_O*l_M*l_N*i_N
fn flat_index(indeces: &Vec<usize>, lengths: &Vec<usize>) -> usize {
let mut idx = 0;
for i in 0..indeces.len() {
idx += indeces[i]*lengths[0..i].iter()
.fold(1, |state, &l| { state * l });
}
idx
fn flat_index(indeces: &[usize], lengths: &[usize]) -> usize {
indeces.iter().enumerate().map(|(i, idx)| {
idx * lengths.iter().take(i).product::<usize>()
}).sum()
}
// returns true if the values are inside the histogram boundaries defined by cfg
@@ -111,83 +244,142 @@ fn is_in_hist_boundaries(values: &[f64], cfg: &Config) -> bool {
}
// returns true given time in inside the time boundaries defined by cfg
fn is_in_time_boundaries(time: f64, cfg: &Config) -> bool {
if cfg.start <= time && time <= cfg.end {
fn is_in_time_boundaries(time: f64, start: f64, end: f64) -> bool {
if start <= time && time <= end {
return true
}
false
}
// parse a timeseries file into a histogram
fn read_window_file(window_file: &str, cfg: &Config) -> Result<Histogram> {
let f = File::open(window_file)
// parse a time series file
fn read_window_file(window_file: &str, cfg: &Config) -> Result<(Vec<Vec<f64>>, usize)> {
let mut timeseries: Vec<Vec<f64>> = read_timeseries(window_file, cfg)?;
// filter the timeseries based on start/end parameters
let time_series_mask: Vec<bool> = timeseries[0].iter()
.map(|t| is_in_time_boundaries(*t, cfg.start, cfg.end)).collect();
timeseries = timeseries.into_iter().map(|ts| {
ts.into_iter().zip(time_series_mask.iter()).filter_map(|(val, mask)| {
if *mask {
Some(val)
} else {
None
}
}).collect()
}).collect::<Vec<Vec<f64>>>();
let timeseries_inital_length = timeseries[0].len();
if cfg.uncorr {
timeseries = uncorrelate(timeseries, cfg);
}
if timeseries[0].is_empty() {
bail!("Time series is empty")
}
Ok((timeseries, timeseries_inital_length))
}
// Read a multidimensional timeseries
// The resulting vector contains one vector per dimension
fn read_timeseries(window_file: &str, cfg: &Config) -> Result<Vec<Vec<f64>>> {
let f = File::open(window_file)
.chain_err(|| format!("Failed to open sample data file {}.", window_file))?;
let mut buf = BufReader::new(&f);
// total number of bins is the product of all dimensions length
let total_bins = cfg.num_bins.iter().fold(1, |s, &x| { s*x });
let mut hist = vec![0.0; total_bins];
// bin width for each dimension: (max-min)/bins
let bin_width: Vec<f64> = (0..cfg.dimens).map(|idx| {
(cfg.hist_max[idx] - cfg.hist_min[idx])/(cfg.num_bins[idx] as f64)
}).collect();
let mut timeseries = vec![Vec::new(); cfg.dimens+1];
// read and parse each timeseries line
let mut line = String::new();
let mut linecount = 0;
while buf.read_line(&mut line).chain_err(|| "Failed to read line")? > 0 {
linecount += 1;
// skip comments and empty lines
if line.starts_with("#") || line.starts_with("@") || line.len() == 0 {
if line.starts_with('#') || line.starts_with('@') || line.is_empty() {
line.clear();
continue;
}
{
let split: Vec<&str> = line.split_whitespace().collect();
if split.len() < cfg.dimens+1 {
bail!(format!("Wrong number of columns in line {} of window file {}. Empty Line?.", linecount, window_file));
}
let mut values: Vec<f64> = vec![f64::NAN; cfg.dimens+1];
for i in 0..values.len() {
values[i] = split[i].parse::<f64>()
.chain_err(|| format!("Failed to parse line {} of window file {}.", linecount, window_file))?;
}
for i in 0..cfg.dimens+1 {
timeseries[i].push(split[i].parse::<f64>()
.chain_err(|| format!("Failed to parse line {} of window file {}.", linecount, window_file))?
if is_in_hist_boundaries(&values[1..], cfg) && is_in_time_boundaries(values[0], cfg) {
let bin_indeces = (0..cfg.dimens).map(|dimen: usize| {
let val = values[dimen+1];
((val - cfg.hist_min[dimen]) / bin_width[dimen]) as usize
}).collect();
let index = flat_index(&bin_indeces, &cfg.num_bins);
hist[index] += 1.0;
);
}
}
line.clear();
}
Ok(timeseries)
}
let num_points: f64 = hist.iter().sum();
Ok(Histogram::new(num_points as u32, hist))
// calculates the inefficiency for every collective variable
// filters the timeseries based on the highest inefficiency
fn uncorrelate(timeseries: Vec<Vec<f64>>, cfg: &Config) -> Vec<Vec<f64>> {
// calculate inefficiencies and find the highest one
let gs: Vec<f64> = timeseries[1..].iter().map(|ts| statistical_ineff(ts)).collect();
let mut max_g = 1.0;
for g in gs {
if g > max_g {
max_g = g;
}
}
// round g up
let mut trunc_g = max_g.trunc() as usize;
if (trunc_g as f64 - max_g).abs() > 0.000_000_000_1 {
trunc_g += 1;
}
// filter correlated samples from timeseries
let prev_len = timeseries[0].len();
let timeseries = timeseries.into_iter().map(|ts| {
ts.into_iter().step_by(trunc_g).collect::<Vec<f64>>()
}).collect::<Vec<Vec<f64>>>();
let new_len = timeseries[0].len();
if cfg.verbose {
let tau = autocorrelation_time(max_g)* (timeseries[0][1]-timeseries[0][0]);
vprintln(format!("{:?}/{:?} samples are uncorrelated. {:?} samples removed from timeseries (tau={:.5})", new_len, prev_len, prev_len-new_len, tau), true);
}
timeseries
}
// Write WHAM calculation results to out_file.
pub fn write_results(out_file: &str, ds: &Dataset, free: &Vec<f64>, free_std: &Vec<f64>, prob: &Vec<f64>, prob_std: &Vec<f64>) -> Result<()> {
let output = File::create(out_file)
pub fn write_results(out_file: &str, append: bool, ds: &Dataset, free: &[f64],
free_std: &[f64], prob: &[f64], prob_std: &[f64], index: Option<usize>) -> Result<()> {
if !append && Path::new(out_file).exists() {
std::fs::remove_file(out_file).chain_err(|| "Failed to delete file.")?;
}
let output = OpenOptions::new().write(true)
.append(true)
.create(true)
.open(out_file)
.chain_err(|| format!("Failed to create file with path {}", out_file))?;
let mut buf = BufWriter::new(output);
let header: String = (0..ds.dimens_lengths.len()).map(|d| format!("coord{}", d+1))
.collect::<Vec<String>>().join(" ");
writeln!(buf, "#{} {} {} {} {}", header, "Free Energy", "+/-", "Probability", "+/-");
if let Some(index) = index {
writeln!(buf, "#Dataset {}", index).unwrap();
}
writeln!(buf, "#{} Free Energy +/- Probability +/-", header).unwrap();
for bin in 0..free.len() {
let coords = ds.get_coords_for_bin(bin);
let coords_str: String = coords.iter().map(|c| {format!("{:8.6} ", c)})
.collect::<Vec<String>>().join("\t");
writeln!(buf, "{}{:8.6} {:8.6} {:8.6} {:8.6}", coords_str, free[bin], free_std[bin], prob[bin], prob_std[bin])
writeln!(buf, "{}{:8.6} {:8.6} {:8.6} {:8.6}", coords_str,
free[bin], free_std[bin], prob[bin], prob_std[bin])
.chain_err(|| "Failed to write to file.")?;
}
Ok(())
@@ -196,10 +388,12 @@ pub fn write_results(out_file: &str, ds: &Dataset, free: &Vec<f64>, free_std: &V
#[cfg(test)]
mod tests {
use super::*;
use assert_approx_eq::assert_approx_eq;
fn cfg() -> Config {
Config {
metadata_file: "example/1d_cyclic/metadata.dat".to_string(),
hist_min: vec![-3.14],
hist_max: vec![3.14],
num_bins: vec![10],
@@ -213,7 +407,9 @@ mod tests {
bootstrap: 0,
bootstrap_seed: 1234,
start: 0.0,
end: 1e+20
end: 1e+20,
uncorr: false,
convdt: 0.0,
}
}
@@ -221,27 +417,50 @@ mod tests {
fn read_window_file() {
let f = "example/1d_cyclic/COLVAR+0.0.xvg";
let cfg = cfg();
let h = super::read_window_file(&f, &cfg).unwrap();
let (timeseries, timeseries_inital_length) = super::read_window_file(&f, &cfg).unwrap();
let mask = vec![true; timeseries[0].len()];
let h = build_histogram_from_timeseries(&timeseries, &mask, &cfg);
println!("{:?}", h);
assert_eq!(5000, timeseries_inital_length);
assert_eq!(5000, h.num_points);
assert_eq!(0.0, h.bins[2]);
assert_eq!(11.0, h.bins[3]);
assert_eq!(2236.0, h.bins[4]);
assert_eq!(2714.0, h.bins[5]);
assert_eq!(39.0, h.bins[6]);
assert_eq!(0.0, h.bins[7]);
assert_approx_eq!(0.0, h.bins[2]);
assert_approx_eq!(11.0, h.bins[3]);
assert_approx_eq!(2236.0, h.bins[4]);
assert_approx_eq!(2714.0, h.bins[5]);
assert_approx_eq!(39.0, h.bins[6]);
assert_approx_eq!(0.0, h.bins[7]);
}
#[test]
fn read_timeseries() {
let f = "example/1d_cyclic/COLVAR+0.0.xvg";
let cfg = cfg();
let ts = super::read_timeseries(&f, &cfg).unwrap();
let expected = [
-0.153_145,
-0.377_860,
0.010_992,
0.123_074,
0.108_291,
0.261_607,
];
assert!(ts.len() == 2);
assert!(ts[0].len() == 5000);
println!("{:?}", ts);
for (actual, expected) in ts[1].iter().zip(expected.iter()) {
assert!((actual-expected).abs() < 0.001, format!("{:?} != {:?}", actual, expected));
}
}
#[test]
fn read_data() {
let cfg = cfg();
let ds = super::read_data(&cfg).unwrap();
let ds = &super::read_data(&cfg).unwrap()[0];
println!("{:?}", ds);
assert_eq!(25, ds.num_windows);
assert_eq!(cfg.num_bins.len(), ds.dimens_lengths.len());
assert_eq!(cfg.num_bins[0], ds.dimens_lengths[0]);
assert_eq!(cfg.temperature * k_B, ds.kT);
assert_approx_eq!(cfg.temperature * k_B, ds.kT);
assert_eq!(25, ds.histograms.len())
}
@@ -255,4 +474,73 @@ mod tests {
let relative3 = super::get_relative_path(&path1, &path3);
assert_eq!("path/to/subfolder/another_file.dat" ,relative3);
}
#[test]
fn is_in_time_boundaries() {
let start = 10.0;
let end = 20.0;
assert!(super::is_in_time_boundaries(15.0, start, end));
assert!(super::is_in_time_boundaries(10.0, start, end));
assert!(super::is_in_time_boundaries(20.0, start, end));
assert!(!super::is_in_time_boundaries(9.9999999, start, end));
assert!(!super::is_in_time_boundaries(20.000001, start, end));
}
#[test]
fn get_convdt_boundaries() {
let mut cfg = cfg();
let timeseries: Vec<f64> = (0..31).map(|i| i as f64).collect();
println!("{:?}", timeseries);
cfg.start = 10.0;
cfg.end = 20.0;
cfg.convdt = 10.0;
let test = super::get_convdt_boundaries(&timeseries, &cfg);
println!("{:?}", test);
assert!(test.len() == 1);
assert_approx_eq!(test[0].0, 10.0);
assert_approx_eq!(test[0].1, 20.0);
cfg.start = 10.0;
cfg.end = 20.0;
cfg.convdt = 5.0;
let test = super::get_convdt_boundaries(&timeseries, &cfg);
println!("{:?}", test);
assert!(test.len() == 2);
assert_approx_eq!(test[0].0, 10.0);
assert_approx_eq!(test[0].1, 15.0);
assert_approx_eq!(test[1].0, 10.0);
assert_approx_eq!(test[1].1, 20.0);
let timeseries: Vec<f64> = (10..21).map(|i| i as f64).collect();
println!("{:?}", timeseries);
cfg.start = 10.0;
cfg.end = 20.0;
cfg.convdt = 10.0;
let test = super::get_convdt_boundaries(&timeseries, &cfg);
println!("{:?}", test);
assert!(test.len() == 1);
assert_approx_eq!(test[0].0, 10.0);
assert_approx_eq!(test[0].1, 20.0);
cfg.start = 5.0;
cfg.end = 20.0;
cfg.convdt = 10.0;
let test = super::get_convdt_boundaries(&timeseries, &cfg);
println!("{:?}", test);
assert!(test.len() == 1);
assert_approx_eq!(test[0].0, 10.0);
assert_approx_eq!(test[0].1, 20.0);
cfg.start = 5.0;
cfg.end = 30.0;
cfg.convdt = 10.0;
let test = super::get_convdt_boundaries(&timeseries, &cfg);
println!("{:?}", test);
assert!(test.len() == 1);
assert_approx_eq!(test[0].0, 10.0);
assert_approx_eq!(test[0].1, 20.0);
}
}

View File

@@ -3,12 +3,17 @@
#[macro_use]
extern crate error_chain;
extern crate rand;
extern crate rgsl;
extern crate rayon;
#[cfg(test)]
#[macro_use]
extern crate assert_approx_eq;
pub mod io;
pub mod histogram;
pub mod error_analysis;
pub mod correlation_analysis;
pub mod statistics;
use histogram::Dataset;
use std::f64;
@@ -21,7 +26,7 @@ pub mod errors { error_chain!{} }
use errors::*;
#[allow(non_upper_case_globals)]
static k_B: f64 = 0.0083144621; // kJ/mol*K
static k_B: f64 = 0.008_314_462_1; // kJ/mol*K
// Application config
#[derive(Debug)]
@@ -41,14 +46,21 @@ pub struct Config {
pub bootstrap_seed: u64,
pub start: f64,
pub end: f64,
pub uncorr: bool,
pub convdt: f64,
pub ignore_empty: bool
}
impl fmt::Display for Config {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Metadata={}, hist_min={:?}, hist_max={:?}, bins={:?} verbose={}, tolerance={}, iterations={}, temperature={}, cyclic={:?}, bootstrap={:?}, seed={:?}",
write!(f, "Metadata={}, hist_min={:?}, hist_max={:?}, bins={:?},
verbose={}, tolerance={}, iterations={}, temperature={},
cyclic={:?}, uncorr={:?}, bootstrap={:?}, seed={:?},
uncorr={:?}, start={:?}, end={:?}, convdt={:?}, ignore_empty={:?}",
self.metadata_file, self.hist_min, self.hist_max, self.num_bins,
self.verbose, self.tolerance, self.max_iterations, self.temperature,
self.cyclic, self.bootstrap, self.bootstrap_seed)
self.cyclic, self.uncorr, self.bootstrap, self.bootstrap_seed,
self.uncorr, self.start, self.end, self.convdt, self.ignore_empty)
}
}
@@ -56,25 +68,34 @@ impl fmt::Display for Config {
// converged if the maximal difference for the calculated bias offsets is
// smaller then a tolerance value.
fn is_converged(old_F: &[f64], new_F: &[f64], tolerance: f64) -> bool {
!new_F.iter().zip(old_F.iter())
.map(|x| { (x.0-x.1).abs() })
.any(|diff| { diff > tolerance })
// calculates abs diff between every old and new F and checks if any
// is larger than tolerance
!new_F.iter()
.zip(old_F.iter())
.map(|x| { (x.0-x.1).abs() })
.any(|diff| { diff > tolerance })
}
// estimate the probability of a bin of the histogram set based on given bias offsets (F)
// This evaluates the first WHAM equation for each bin.
// estimate the probability of a bin of the histogram set based on given bias
// offsets (F). This evaluates the first WHAM equation for each bin:
// P(x) = \frac {\sum_{i=1}^N{n_i(x)}}
// {\sum_{i=1}^N{ N_i exp(\beta [F_i - U_{bias,i}(x)])}}
fn calc_bin_probability(bin: usize, dataset: &Dataset, F: &[f64]) -> f64 {
let mut denom_sum: f64 = 0.0;
let bin_count: f64 = dataset.get_weighted_bin_count(bin);
for (window, h) in dataset.histograms.iter().enumerate() {
let bias = dataset.get_bias(bin, window);
denom_sum += (dataset.weights[window] * h.num_points as f64) * bias * F[window];
denom_sum += (dataset.weights[window] * h.num_points as f64)
* bias * F[window];
}
bin_count / denom_sum
}
// estimate the bias offset F of the histogram based on given probabilities.
// This evaluates the second WHAM equation for each window and returns exp(F/kT)
// This evaluates the second WHAM equation for each window and returns exp(F/kT).
// exp(F/kT) is not required in intermediate steps so we save some time by not
// calculating it for every iteration.
// F_i = - 1/\beta ln[\sum_{X_{bins}}{P(x)exp(-\beta U_{bias,i}(x))}]
fn calc_window_F(window: usize, dataset: &Dataset, P: &[f64]) -> f64 {
let f: f64 = (0..dataset.num_bins).zip(P.iter()) // zip bins and P
.map(|bin_and_prob: (usize, &f64)| {
@@ -84,16 +105,18 @@ fn calc_window_F(window: usize, dataset: &Dataset, P: &[f64]) -> f64 {
1.0/f
}
// One full WHAM iteration includes calculation of new probabilities P and
// new bias offsets F based on previous bias offsets F_prev. This updates
// the values in vectors F and P
// One full WHAM iteration: calculation of new probabilities P and new bias
// offsets F based on previous bias offsets F_prev. This updates the values in
// vectors F and P.
fn perform_wham_iteration(dataset: &Dataset, F_prev: &[f64], F: &mut Vec<f64>, P: &mut Vec<f64>) {
// evaluate first WHAM equation for each bin to
// estimage probabilities based on previous offsets (F_prev))
(0..dataset.num_bins).into_par_iter()
// Update P
// evaluate first WHAM equation for each bin to
// estimate probabilities based on previous offsets (F_prev))
(0..dataset.num_bins).into_par_iter()
.map(|bin| { calc_bin_probability(bin, dataset, F_prev) })
.collect_into_vec(P);
// Update F
// evaluate second WHAM equation for each window to
// estimate new bias offsets from propabilities
(0..dataset.num_windows).into_par_iter()
@@ -101,12 +124,20 @@ fn perform_wham_iteration(dataset: &Dataset, F_prev: &[f64], F: &mut Vec<f64>, P
.collect_into_vec(F);
}
pub fn perform_wham(cfg: &Config, dataset: &Dataset) -> Result<(Vec<f64>, Vec<f64>, Vec<f64>)> {
// Full WHAM calculation. Calls `perform_wham_iteration` until convergence
// criteria are met or max iterations reached.
pub fn perform_wham(cfg: &Config, dataset: &Dataset)
-> Result<(Vec<f64>, Vec<f64>, Vec<f64>)> {
// allocate required vectors.
let mut P: Vec<f64> = vec![f64::NAN; dataset.num_bins]; // bin probability
let mut F: Vec<f64> = vec![1.0; dataset.num_windows]; // bias offset exp(F/kT)
let mut F_prev: Vec<f64> = vec![f64::NAN; dataset.num_windows]; // previous bias offset
let mut F_tmp: Vec<f64> = vec![f64::NAN; dataset.num_windows]; // temp storage for F
// bin probability
let mut P: Vec<f64> = vec![f64::NAN; dataset.num_bins];
// bias offset exp(F/kT)
let mut F: Vec<f64> = vec![1.0; dataset.num_windows];
// previous bias offset
let mut F_prev: Vec<f64> = vec![f64::NAN; dataset.num_windows];
// temp storage for F
let mut F_tmp: Vec<f64> = vec![f64::NAN; dataset.num_windows];
let mut iteration = 0;
let mut converged = false;
@@ -118,33 +149,32 @@ pub fn perform_wham(cfg: &Config, dataset: &Dataset) -> Result<(Vec<f64>, Vec<f6
// store F values before the next iteration
F_prev.copy_from_slice(&F);
// perform wham iteration (this updates F and P)
// perform wham iteration (this updates F and P).
perform_wham_iteration(&dataset, &F_prev, &mut F, &mut P);
// convergence check
if iteration % 10 == 0 {
// This backups exp(F/kT) in a temporary vector and calculates true F and F_prev for
// convergence. Finally, F is restored. F_prev does not need to be restored because
// its overwritten for the next iteration.
// This backups exp(F/kT) in a temporary vector and calculates
// true F and F_prev for convergence. Finally, F is restored.
// F_prev does not need to be restored because its overwritten
// for the next iteration.
F_tmp.copy_from_slice(&F);
for f in F.iter_mut() { *f = -dataset.kT * f.ln() }
for f in F_prev.iter_mut() { *f = -dataset.kT * f.ln() }
converged = is_converged(&F_prev, &F, cfg.tolerance);
println!("Iteration {}: dF={}", &iteration, &diff_avg(&F_prev, &F));
if cfg.verbose {
println!("Iteration {}: dF={}", &iteration, &diff_avg(&F_prev, &F));
}
F.copy_from_slice(&F_tmp);
}
// Dump free energy and bias offsets
//if iteration % 100 == 0 {
// free_energy(&histograms, &mut P, &mut A);
// dump_state(&histograms, &F, &F_prev, &P, &A);
//}
}
// Normalize P to sum(P) = 1.0
let P_sum: f64 = P.iter().sum();
P.iter_mut().map(|p| *p /= P_sum).count();
for p in P.iter_mut() {
*p /= P_sum;
}
if iteration == cfg.max_iterations {
bail!("WHAM not converged! (max iterations reached)");
@@ -157,30 +187,40 @@ pub fn run(cfg: &Config) -> Result<()>{
println!("Supplied WHAM options: {}", &cfg);
println!("Reading input files.");
let dataset = io::read_data(&cfg).chain_err(|| "Failed to create histogram.")?;
println!("{}", &dataset);
let datasets = io::read_data(&cfg).chain_err(|| "Failed to read data.")?;
let (P, F, F_prev) = perform_wham(&cfg, &dataset)?;
for (idx, dataset) in datasets.iter().enumerate() {
if datasets.len() > 1 {
println!("Dataset {}/{}: {}", idx+1, datasets.len(), &dataset);
}
else {
println!("{}", &dataset);
}
let (P, F, F_prev) = perform_wham(&cfg, &dataset)?;
println!("WHAM converged.");
let P_std: Vec<f64>;
let free_energy_std: Vec<f64>;
if cfg.bootstrap > 0 {
let error_est = error_analysis::run_bootstrap(&cfg, dataset.clone(), &P, cfg.bootstrap);
P_std = error_est.0;
free_energy_std = error_est.1;
} else {
P_std = vec![0.0; P.len()];
free_energy_std = vec![0.0; P.len()];
}
let (P_std, free_energy_std) = if cfg.bootstrap > 0 {
println!("Bootstrapping..");
error_analysis::run_bootstrap(&cfg, dataset.clone(), cfg.bootstrap)
} else {
(vec![0.0; P.len()], vec![0.0; P.len()])
};
// calculate free energy and dump state
println!("Finished. Dumping final PMF");
let free_energy = calc_free_energy(&dataset, &P);
dump_state(&dataset, &F, &F_prev, &P, &P_std, &free_energy, &free_energy_std);
io::write_results(&cfg.output, &dataset, &free_energy, &free_energy_std, &P, &P_std)
.chain_err(|| "Could not write results to output file")?;
// calculate free energy and dump state
println!("Finished. Dumping PMF");
let free_energy = calc_free_energy(&dataset, &P);
dump_state(&dataset, &F, &F_prev, &P, &P_std, &free_energy, &free_energy_std);
let append = idx > 0 && datasets.len() > 1;
let index = if datasets.len() > 1 {
Some(idx)
} else {
None
};
io::write_results(&cfg.output, append, &dataset, &free_energy, &free_energy_std, &P, &P_std, index)
.chain_err(|| "Could not write results to output file")?;
}
Ok(())
}
@@ -214,19 +254,23 @@ fn calc_free_energy(dataset: &Dataset, P: &[f64]) -> Vec<f64> {
free_energy
}
fn dump_state(dataset: &Dataset, F: &[f64], F_prev: &[f64], P: &[f64], P_std: &[f64], A: &[f64], A_std: &[f64]) {
// Print the current WHAM iteration state. Dumps the PMF and associated vectors
fn dump_state(dataset: &Dataset, F: &[f64], F_prev: &[f64], P: &[f64],
P_std: &[f64], A: &[f64], A_std: &[f64]) {
// TODO fix output of F/F_prev
let out = std::io::stdout();
let mut lock = out.lock();
writeln!(lock, "# PMF");
writeln!(lock, "#bin\t\tFree Energy\t\t+/-\t\tP(x)\t\t+/-");
writeln!(lock, "# PMF").unwrap();
writeln!(lock, "#bin\t\tFree Energy\t\t+/-\t\tP(x)\t\t+/-").unwrap();
for bin in 0..dataset.num_bins {
writeln!(lock, "{:9.5}\t{:9.5}\t{:9.5}\t{:9.5}\t{:9.5}", bin, A[bin], A_std[bin], P[bin], P_std[bin]);
writeln!(lock, "{:9.5}\t{:9.5}\t{:9.5}\t{:9.5}\t{:9.5}",
bin, A[bin], A_std[bin], P[bin], P_std[bin]).unwrap();
}
writeln!(lock, "# Bias offsets");
writeln!(lock, "#Window\t\tF\t\tF_prev");
writeln!(lock, "# Bias offsets").unwrap();
writeln!(lock, "#Window\t\tF\t\tF_prev").unwrap();
for window in 0..dataset.num_windows {
writeln!(lock, "{}\t{:9.5}\t{:8.8}", window, F[window], (F[window]-F_prev[window]).abs());
writeln!(lock, "{}\t{:9.5}\t{:8.8}",
window, F[window], (F[window]-F_prev[window]).abs()).unwrap();
}
}
@@ -268,23 +312,23 @@ mod tests {
fn calc_bin_probability() {
let dataset = create_test_dataset();
let F = vec![1.0; dataset.num_bins] ;
let expected = vec!(0.0, 0.0825296687031316, 40.92355847097493,
124226.70003377, 2308526035.5283747);
for b in 0..dataset.num_bins {
let p = super::calc_bin_probability(b, &dataset, &F);
assert_delta!(expected[b], p, 0.0000001);
}
}
let expected = vec!(0.0, 0.082_529_668_703_131_6, 40.923_558_470_974_93,
124_226.700_033_77, 2_308_526_035.528_374_7);
expected.iter().enumerate().for_each(|(i, exp)| {
let p = super::calc_bin_probability(i, &dataset, &F);
assert_delta!(exp, p, 0.000_000_1);
})
}
#[test]
fn calc_bias_offset() {
let dataset = create_test_dataset();
let probability = vec!(0.0, 0.1, 0.2, 0.3, 0.4);
let expected = vec!(15.927477169990633, 15.927477169990633);
for window in 0..dataset.num_windows {
let F = super::calc_window_F(window, &dataset, &probability);
assert_delta!(expected[window], F, 0.0000001);
}
let expected = vec!(15.927_477_169_990_633, 15.927_477_169_990_633);
expected.iter().enumerate().for_each(|(i, exp)| {
let F = super::calc_window_F(i, &dataset, &probability);
assert_delta!(exp, F, 0.000_000_1);
})
}
#[test]
@@ -295,8 +339,8 @@ mod tests {
let mut P = vec![f64::NAN; dataset.num_bins];
super::perform_wham_iteration(&dataset, &prev_F, &mut F, &mut P);
let expected_F = vec!(1.0, 1.0);
let expected_P = vec!(0.0, 0.0825296687031316, 40.92355847097493,
124226.70003377, 2308526035.5283747);
let expected_P = vec!(0.0, 0.082_529_668_703_131_6, 40.923_558_470_974_93,
124_226.700_033_77, 2_308_526_035.528_374_7);
for bin in 0..dataset.num_bins {
assert_delta!(expected_P[bin], P[bin], 0.01)
}

View File

@@ -3,6 +3,7 @@ extern crate wham;
extern crate clap;
extern crate rand;
use rand::prelude::*;
use clap::App;
use wham::Config;
use wham::errors::*;
@@ -49,7 +50,6 @@ fn cli() -> Result<Config> {
.chain_err(|| "Cannot parse bootstrap iteration.")?;
let bootstrap_seed: u64 = matches.value_of("bootstrap_seed")
.unwrap_or({
use rand::Rng;
let mut rng = rand::thread_rng();
&rng.gen::<u32>().to_string()
}).parse()
@@ -58,6 +58,8 @@ fn cli() -> Result<Config> {
.chain_err(|| "Cannot parse start time.")?;
let end: f64 = matches.value_of("end").unwrap_or("1e+20").parse()
.chain_err(|| "Cannot parse end time.")?;
let uncorr: bool = matches.is_present("uncorr");
if num_bins.len() != hist_max.len() || num_bins.len() != hist_max.len() {
eprintln!("Input dimensions do not match (min: {}, max: {}, bins: {})",
@@ -66,10 +68,14 @@ fn cli() -> Result<Config> {
}
let dimens = num_bins.len();
let convdt: f64 = matches.value_of("convdt").unwrap_or("0").parse()
.chain_err(|| "Cannot parse convdt.")?;
let ignore_empty: bool = matches.is_present("ignore_empty");
Ok(wham::Config{metadata_file, hist_min, hist_max, num_bins, dimens,
verbose, tolerance, max_iterations, temperature, cyclic, output,
bootstrap, bootstrap_seed, start, end})
bootstrap, bootstrap_seed, start, end, uncorr, convdt, ignore_empty})
}
fn main() {

59
src/statistics.rs Normal file
View File

@@ -0,0 +1,59 @@
pub fn mean(x: &[f64]) -> f64 {
x.iter().sum::<f64>() / x.len() as f64
}
pub fn autocov(x: &[f64]) -> f64 {
let x_mean = mean(x);
x.iter().map(|xi| {
(xi-x_mean).powi(2)
}).sum::<f64>() / x.len() as f64
}
pub fn sd(x: &[f64]) -> f64 {
let x_mean = mean(x);
let n = x.len() as f64;
let sum = x.iter().map(|xi| {
(xi-x_mean).powi(2)
}).sum::<f64>();
(1.0/(n-1.0) * sum).sqrt()
}
#[cfg(test)]
mod tests {
use assert_approx_eq::assert_approx_eq;
// a sine wave
fn dataset() -> Vec<f64> {
(0..100).map(|i| (i as f64 / 100.0 * std::f64::consts::PI).sin())
.collect::<Vec<f64>>()
}
#[test]
fn mean() {
let ds = dataset();
let expected =0.6366;
let m = super::mean(&ds);
assert_approx_eq!(m, expected, 0.0001);
}
#[test]
fn autocorr() {
let ds = dataset();
let expected = 0.094_782;
let m = super::autocov(&ds);
assert_approx_eq!(m, expected, 0.000_001);
}
#[test]
fn sd() {
let ds = dataset();
let expected = 0.309_418;
let m = super::sd(&ds);
assert_approx_eq!(m, expected, 0.000_001);
}
}

View File

@@ -2,8 +2,6 @@ mod command;
#[cfg(test)]
mod integration {
use std::process::Command;
use super::command::get_command;
#[test]

View File

@@ -27,6 +27,26 @@ mod integration {
assert_eq!(output_len, 0);
}
#[test]
fn wham_1d_cyclic_uncorrelated() {
get_command()
.args(&["--bins", "100", "--max", "pi", "--min", "-pi", "-T", "300", "--cyclic", "--uncorr"])
.args(&["--seed", "1234"])
.args(&["-f", "example/1d_cyclic/metadata.dat"])
.args(&["-o", "/tmp/wham_test_1d_cyclic.out"])
.output()
.expect("failed to execute process");
assert!(fs::metadata("/tmp/wham_test_1d_cyclic.out").is_ok());
let output = Command::new("diff")
.arg("/tmp/wham_test_1d_cyclic.out")
.arg("example/1d_cyclic/wham_uncorrelated.out")
.output()
.expect("failed to run diff");
let output_len = String::from_utf8_lossy(&output.stdout).len();
assert_eq!(output_len, 0);
}
#[test]
#[ignore] // expensive
fn wham_2d_cyclic() {
@@ -46,4 +66,4 @@ mod integration {
let output_len = String::from_utf8_lossy(&output.stdout).len();
assert_eq!(output_len, 0);
}
}
}