Created
February 5, 2016 22:24
-
-
Save swashcap/b5b9a3cf6b38583c303d to your computer and use it in GitHub Desktop.
COINSTAC Stuff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const _ = require('lodash'); | |
const coinstacCommon = require('coinstac-common'); | |
const coinstacAlgorithms = require('coinstac-distributed-algorithm-set'); | |
const freesurferParser = require('freesurfer-parser'); | |
const helpers = require('./path/to/helpers/'); | |
const Analysis = coinstacCommon.Analysis; | |
const ComputationPipeline = coinstacCommon.ComputationPipeline; | |
const LocalComputation = coinstacCommon.LocalComputation; | |
const LocalFileComputation = coinstacCommon.LocalFileComputation; | |
const RemoteComputation = coinstacCommon.RemoteComputation; | |
/** | |
* Compute regression on the client. | |
* | |
* @param {array} xVals ? | |
* @param {array} yVals ? | |
* @param {array} aggregateMVals ? | |
* @param {string[]} roiKeys Targetted predictors | |
* @returns {object} | |
*/ | |
function clientComputeRegression(xVals, yVals, aggregateMVals, roiKeys) { | |
// @TODO dep vars (control/patient) must have both types | |
const normalizedYVals = coinstacAlgorithms.utils.normalize(yVals); | |
const normalizedXVals = coinstacAlgorithms.utils.normalize(xVals); | |
const objectiveScore = coinstacAlgorithms.ridgeRegression.objective( | |
aggregateMVals, | |
normalizedXVals, | |
normalizedYVals | |
); | |
const predictedYVals = coinstacAlgorithms.ridgeRegression.applyModel( | |
aggregateMVals, | |
normalizedXVals | |
); | |
const gradient = coinstacAlgorithms.ridgeRegression.gradient( | |
aggregateMVals, | |
normalizedXVals, | |
normalizedYVals | |
); | |
return { | |
gradient: _.zipObject(roiKeys, gradient), | |
objective: coinstacAlgos.ridgeRegression.objective( | |
aggregateMVals, | |
normalizedXVals, | |
normalizedYVals | |
), | |
r2: utils.r2(normalizedYVals, predictedYVals), | |
}; | |
} | |
module.exports = { | |
_id: 'freesurfer-multishot', | |
analysis: new Analysis({ | |
name: 'FreeSurfer Multi-Shot', | |
definition: { | |
epsilon: 1, | |
roiKeys: ['Left-Hippocampus'], | |
tolerance: 1e-5, | |
}, | |
description: 'This is a multi-iterative analysis based on…', | |
}), | |
consortium: 'left-hippocampus-buffs', | |
version: '1.0.0', | |
/** | |
* The parent `ComputationPipeline` will do a type check on all its | |
* arguments to determine their params and what to pass their functions. | |
*/ | |
local: new ComputationPipeline([ | |
/** | |
* Passes a set of files, set by the user via UI or command line. | |
* | |
* @param {object} definition Analysis's definition | |
* @param {string[]} files Collection of file paths | |
* @param {function} cb Callback for non-Promise folks | |
* @returns {Promise} | |
*/ | |
new LocalFileComputation(function(definition, files, cb) { | |
return Promise.all(files.map(filePath => { | |
return new Promise((resolve, reject) => { | |
fs.readFileAsync(filePath, 'utf8', (err, data) => { | |
if (err) { | |
return reject(err); | |
} | |
resolve(new FreeSurfer({ | |
string: data.toString(), | |
})); | |
})); | |
}) | |
.then(rois => { | |
const roiKeys = definition.roiKeys; | |
return { | |
file: filePath, | |
/** | |
* Freesurfer files have regions of interest (ROIs) | |
* separated by line. Each line contains a ROI and | |
* its value, separated by a tab. | |
* | |
* @todo Come up with a Freesurfer parser helper | |
*/ | |
rois: rois | |
.split('\n') | |
.reduce((all, line) => { | |
const pieces = line.split('\t'); | |
if (roiKeys.indexOf(pieces[0]) !== -1) { | |
all[pieces[0]] = pieces[1]; | |
} | |
return all; | |
}, {}); | |
}; | |
}) | |
.then(thing => { | |
//TODO: ??? | |
return computeRegression( | |
// xVals | |
// yVals | |
// aggregateMVal | |
definition.roiKeys | |
); | |
}); | |
}) | |
.then(results => { | |
cb(null, results); | |
return results; | |
}, error => { | |
cb(error); | |
throw error; | |
}); | |
}), | |
/** | |
* Regular local computation. Use after extracting data from files. | |
*/ | |
new LocalComputation( | |
function(definition, previousResult, remoteResult, next) { | |
return computeRegression( | |
// xVals | |
// yVals | |
remoteResult.mVals, // aggregateMVals | |
definition.roiKeys | |
); | |
} | |
), | |
]), | |
remote: new ComputationPipeline([ | |
new RemoteComputation( | |
/** | |
* Get an initial, or seed, document for the server's analysis. This | |
* will get passed as `previousResult` to the `process` iteration | |
* below. | |
* | |
* @todo Consider moving this step to the analysis definition? | |
* | |
* @param {object} definition Analysis's definition | |
* @returns {object} Initial document | |
*/ | |
getInitialDocument: function(definition) { | |
const result = { | |
gradient: {}, | |
learningRate: 0.7, | |
mVals: {}, | |
objective: Infinity, | |
r2: 0, | |
}; | |
definition.roiKeys.forEach(key => { | |
result.gradient[key] = 0; | |
result.mVals[key] = Math.random(); | |
}); | |
return result; | |
}, | |
/** | |
* Process | |
* | |
* @param {object} definition | |
* @param {object} previousResult Result of the previous server | |
* computation | |
* @param {object[]} clientResults Collection of results of clients' | |
* computations | |
* @param {function} next | |
*/ | |
process: function(definition, previousResult, clientResults, next) { | |
const roiKeys = definition.roiKeys; | |
const aggregateObjective = | |
helpers.sum(helpers.getObjectiveValues(clientResults)); | |
const aggregateGradient = coinstacAlgorithms.utils.columnWiseSum( | |
helpers.getGradientValues(clientResults, roiKeys) | |
); | |
const gradient = helpers.zipRoiKeyPairs( | |
aggregateGradient, | |
roiKeys | |
); | |
const learningRate = previousResult.learningRate; | |
const previousBestFit = previousResult.previousBestFit; | |
const history = aggregateDoc.history || []; | |
if ( | |
numeric.norm2(helpers.unzipRoiKeyPairs(gradient, roiKeys)) < | |
definition.tolerance | |
) { | |
return next(); | |
} | |
if (aggregateObjective > previousBestFit.objective) { | |
learningRate /= 2; | |
} | |
return { | |
gradient: gradient, | |
learningRate: learningRate, | |
mVals: helpers.zipRoiKeyPairs( | |
coinstacAlgorithms.ridgeRegression.recalculateMVals( | |
learningRate, | |
helpers.unzipRoiKeyPairs( | |
previousBestFit.mVals, | |
roiKeys), | |
helpers.unzipRoiKeyPairs( | |
previousBestFit.gradient, | |
roiKeys | |
) | |
), | |
roiKeys | |
), | |
objective: aggregateObjective, | |
r2: helpers.mean(_.map(clientResults, 'r2')), | |
}; | |
} | |
), | |
]), | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment