Added neural network trait prediction to genetic analyses.

This commit is contained in:
Simon Sarasova 2024-07-19 17:16:28 +00:00
parent 75331a22d3
commit 21082cfb0d
No known key found for this signature in database
GPG key ID: EEDA4103C9C36944
40 changed files with 3316 additions and 1666 deletions

View file

@ -6,6 +6,7 @@ Small and insignificant changes may not be included in this log.
## Unversioned Changes
* Added neural network trait prediction to genetic analyses. - *Simon Sarasova*
* Improved the Create Genetic Models utility and neural network training code. Models are now able to predict traits with some accuracy. - *Simon Sarasova*
* Improved ReadMe.md. - *Simon Sarasova*
* Improved Seekia's slogan and Whitepaper.md. - *Simon Sarasova*

View file

@ -9,4 +9,4 @@ Many other people have written code for modules which are imported by Seekia. Th
Name | Date Of First Commit | Number Of Commits
--- | --- | ---
Simon Sarasova | June 13, 2023 | 265
Simon Sarasova | June 13, 2023 | 266

View file

@ -424,7 +424,7 @@ func setBuildMateProfilePage_Wealth(window fyne.Window, previousPage func()){
}
isLowerBoundHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setWealthOrIncomeIsLowerBoundExplainerPage(window, currentPage)
setWealthIsLowerBoundExplainerPage(window, currentPage)
})
isLowerBoundCheckRow := container.NewHBox(layout.NewSpacer(), isLowerBoundCheck, isLowerBoundHelpButton, layout.NewSpacer())

View file

@ -674,45 +674,95 @@ func setPolygenicDiseaseLocusRiskWeightProbabilityExplainerPage(window fyne.Wind
}
func setTraitOutcomeScoresExplainerPage(window fyne.Window, previousPage func()){
func setDiscreteTraitNeuralNetworkPredictionExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Outcome Scores")
title := getPageTitleCentered("Help - Neural Network Prediction")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Trait Outcome Scores")
subtitle := getPageSubtitleCentered("Discrete Trait Neural Network Prediction")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("Points are added to an outcome to represent a higher probability.")
description4 := getLabelCentered("Points are subtracted from an outcome to represent a lower probability.")
description5 := getLabelCentered("The more rules that are tested, the higher the accuracy of the result will be.")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Neural Networks and Rules.")
description3 := getLabelCentered("Each trait can be analyzed by either rules or a neural network.")
description4 := getLabelCentered("Neural network prediction is calculated by inputing a genome's loci into a neural network.")
description5 := getLabelCentered("Each trait has multiple outcomes, and the neural network predicts a single outcome.")
description6 := getLabelCentered("The higher the quantity of tested loci, the more accurate the result is.")
description7 := getLabelCentered("The probability that a neural network's prediction is accurate is called its Confidence.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5)
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7)
setPageContent(page, window)
}
func setOffspringTraitOutcomeScoresExplainerPage(window fyne.Window, previousPage func()){
func setOffspringDiscreteTraitNeuralNetworkPredictionExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Outcome Scores")
title := getPageTitleCentered("Help - Neural Network Prediction")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Offspring Trait Outcome Scores")
subtitle := getPageSubtitleCentered("Offspring Discrete Trait Neural Network Prediction")
description1 := getLabelCentered("Couple genetic analyses contain trait analyses for the offspring.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("Points are added to an outcome to represent a higher probability.")
description4 := getLabelCentered("Points are subtracted from an outcome to represent a lower probability.")
description5 := getLabelCentered("The more rules that are tested, the higher the accuracy of the result will be.")
description1 := getLabelCentered("Couple genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Neural Networks and Rules.")
description3 := getLabelCentered("Each trait can be analyzed by either rules or a neural network.")
description4 := getLabelCentered("Neural network prediction is calculated by inputing a genome's loci into a neural network.")
description5 := getLabelCentered("Each trait has multiple outcomes, and the neural network predicts the probability of each outcome.")
description6 := getLabelCentered("The higher the quantity of known loci, the more accurate the result is.")
description7 := getLabelCentered("The probability that a neural network's prediction is accurate is called its Confidence.")
description8 := getLabelCentered("For couples, the Confidence is the average of the confidence of 100 prospective offspring predictions.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5)
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8)
setPageContent(page, window)
}
func setTraitRulesExplainerPage(window fyne.Window, previousPage func()){
func setDiscreteTraitRulesPredictionExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Rules Prediction")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Discrete Trait Rules Prediction")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Neural Networks and Rules.")
description3 := getLabelCentered("Each trait can be analyzed by either rules or a neural network.")
description4 := getLabelCentered("Rule prediction is calculated by testing many predictive rules.")
description5 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description6 := getLabelCentered("Points are added to an outcome to represent a higher probability.")
description7 := getLabelCentered("Points are subtracted from an outcome to represent a lower probability.")
description8 := getLabelCentered("The more rules that are tested, the higher the accuracy of the result will be.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8)
setPageContent(page, window)
}
func setOffspringDiscreteTraitRulesPredictionExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Rules Prediction")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Offspring Discrete Trait Rules Prediction")
description1 := getLabelCentered("Couple genetic analyses contain discrete trait analyses for the offspring.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Neural Networks and Rules.")
description3 := getLabelCentered("Each trait can be analyzed by either rules or a neural network.")
description4 := getLabelCentered("Rule prediction is calculated by testing many predictive rules.")
description5 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description6 := getLabelCentered("Points are added to an outcome to represent a higher probability.")
description7 := getLabelCentered("Points are subtracted from an outcome to represent a lower probability.")
description8 := getLabelCentered("The more rules that are tested, the higher the accuracy of the result will be.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8)
setPageContent(page, window)
}
func setDiscreteTraitRulesExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Trait Rules")
@ -720,8 +770,8 @@ func setTraitRulesExplainerPage(window fyne.Window, previousPage func()){
subtitle := getPageSubtitleCentered("Trait Rules")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("Discrete traits has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
@ -732,16 +782,16 @@ func setTraitRulesExplainerPage(window fyne.Window, previousPage func()){
setPageContent(page, window)
}
func setOffspringTraitRulesExplainerPage(window fyne.Window, previousPage func()){
func setOffspringDiscreteTraitRulesExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Trait Rules")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Trait Rules")
subtitle := getPageSubtitleCentered("Discrete Trait Rules")
description1 := getLabelCentered("Offspring genetic analyses contain trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description1 := getLabelCentered("Offspring genetic analyses contain discrete trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each discrete trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
@ -753,15 +803,15 @@ func setOffspringTraitRulesExplainerPage(window fyne.Window, previousPage func()
setPageContent(page, window)
}
func setTraitNumberOfRulesTestedExplainerPage(window fyne.Window, previousPage func()){
func setDiscreteTraitQuantityOfRulesTestedExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Number Of Rules Tested")
title := getPageTitleCentered("Help - Quantity Of Rules Tested")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Number Of Trait Rules Tested")
subtitle := getPageSubtitleCentered("Quantity Of Trait Rules Tested")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represent the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
@ -775,13 +825,13 @@ func setTraitNumberOfRulesTestedExplainerPage(window fyne.Window, previousPage f
setPageContent(page, window)
}
func setOffspringTraitNumberOfRulesTestedExplainerPage(window fyne.Window, previousPage func()){
func setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Number Of Rules Tested")
title := getPageTitleCentered("Help - Quantity Of Rules Tested")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Offspring Trait Number Of Rules Tested")
subtitle := getPageSubtitleCentered("Offspring Trait Quantity Of Rules Tested")
description1 := getLabelCentered("Offspring genetic analyses contain trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
@ -806,8 +856,8 @@ func setOffspringProbabilityOfPassingTraitRuleExplainerPage(window fyne.Window,
subtitle := getPageSubtitleCentered("Offspring Probability Of Passing Trait Rule")
description1 := getLabelCentered("Offspring genetic analyses contain trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description1 := getLabelCentered("Offspring genetic analyses contain discrete trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each discrete trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
@ -821,8 +871,7 @@ func setOffspringProbabilityOfPassingTraitRuleExplainerPage(window fyne.Window,
setPageContent(page, window)
}
func setPersonPassesTraitRuleExplainerPage(window fyne.Window, previousPage func()){
func setPersonPassesDiscreteTraitRuleExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Trait Rules")
@ -830,8 +879,8 @@ func setPersonPassesTraitRuleExplainerPage(window fyne.Window, previousPage func
subtitle := getPageSubtitleCentered("Person Passes Trait Rule")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("Each discrete trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
@ -842,7 +891,7 @@ func setPersonPassesTraitRuleExplainerPage(window fyne.Window, previousPage func
setPageContent(page, window)
}
func setGenomePassesTraitRuleExplainerPage(window fyne.Window, previousPage func()){
func setGenomePassesDiscreteTraitRuleExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Genome Passes Rule")
@ -850,22 +899,23 @@ func setGenomePassesTraitRuleExplainerPage(window fyne.Window, previousPage func
subtitle := getPageSubtitleCentered("Genome Passes Trait Rule")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
description6 := getLabelCentered("If a person passes a rule, its effects will be applied to their outcome(s).")
description7 := getLabelCentered("If a person has imported multiple genomes, each genome may or may not pass the rule.")
description8 := getLabelCentered("If one genome passes and another does not, it means that one genome has an invalid value.")
description9 := getLabelCentered("This happens because genome sequencing technology is not perfectly accurate.")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Rules and Neural Networks.")
description3 := getLabelCentered("For rule-based analyses, each trait's outcome has an associated score.")
description4 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description5 := getLabelCentered("Each outcome's score is determined by trait rules.")
description6 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
description7 := getLabelCentered("If a person passes a rule, its effects will be applied to their outcome(s).")
description8 := getLabelCentered("If a person has imported multiple genomes, each genome may or may not pass the rule.")
description9 := getLabelCentered("If one genome passes and another does not, it means that one genome has an invalid value.")
description10 := getLabelCentered("This happens because genome sequencing technology is not perfectly accurate.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8, description9)
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8, description9, description10)
setPageContent(page, window)
}
func setTraitRuleOutcomeEffectsExplainerPage(window fyne.Window, previousPage func()){
func setDiscreteTraitRuleOutcomeEffectsExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Outcome Effects")
@ -885,7 +935,7 @@ func setTraitRuleOutcomeEffectsExplainerPage(window fyne.Window, previousPage fu
}
func setWealthOrIncomeIsLowerBoundExplainerPage(window fyne.Window, previousPage func()){
func setWealthIsLowerBoundExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Is Lower Bound")

View file

@ -10,6 +10,7 @@ import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/widget"
import "fyne.io/fyne/v2/canvas"
import "seekia/resources/geneticPredictionModels"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
import "seekia/resources/geneticReferences/traits"
@ -48,7 +49,7 @@ func setViewCoupleGeneticAnalysisPage(window fyne.Window, person1Identifier stri
return
}
if (person1Found == false){
setErrorEncounteredPage(window, errors.New("Couple person A not found."), previousPage)
setErrorEncounteredPage(window, errors.New("Couple person 1 not found."), previousPage)
return
}
@ -58,7 +59,7 @@ func setViewCoupleGeneticAnalysisPage(window fyne.Window, person1Identifier stri
return
}
if (person2Found == false){
setErrorEncounteredPage(window, errors.New("Couple person B not found."), previousPage)
setErrorEncounteredPage(window, errors.New("Couple person 2 not found."), previousPage)
return
}
@ -88,11 +89,15 @@ func setViewCoupleGeneticAnalysisPage(window fyne.Window, person1Identifier stri
polygenicDiseasesButton := widget.NewButton("Polygenic Diseases", func(){
setViewCoupleGeneticAnalysisPolygenicDiseasesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, currentPage)
})
traitsButton := widget.NewButton("Traits", func(){
setViewCoupleGeneticAnalysisTraitsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, currentPage)
discreteTraitsButton := widget.NewButton("Discrete Traits", func(){
setViewCoupleGeneticAnalysisDiscreteTraitsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, currentPage)
})
numericTraitsButton := widget.NewButton("Numeric Traits", func(){
//TODO
showUnderConstructionDialog(window)
})
categoryButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, generalButton, monogenicDiseasesButton, polygenicDiseasesButton, traitsButton))
categoryButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, generalButton, monogenicDiseasesButton, polygenicDiseasesButton, discreteTraitsButton, numericTraitsButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), warningLabel1, warningLabel2, widget.NewSeparator(), coupleNameRow, widget.NewSeparator(), numberOfAnalyzedGenomesLabel, person1NumberOfAnalyzedGenomesRow, person2NumberOfAnalyzedGenomesRow, widget.NewSeparator(), categoryButtonsGrid)
@ -1327,7 +1332,7 @@ func setViewCoupleGeneticAnalysisPolygenicDiseaseGenomePairDetailsPage(window fy
genomeNameLabel := getBoldLabelCentered(genomeName)
personRiskScoreKnown, _, personRiskScoreFormatted, _, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier)
personRiskScoreKnown, _, personRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, personAnalysisGenomeIdentifier)
if (err != nil) { return err }
getPersonRiskScoreLabelText := func()string{
@ -1860,120 +1865,206 @@ func setViewPolygenicDiseaseSampleOffspringRiskScoresChart(window fyne.Window, d
}
func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, previousPage func()){
func setViewCoupleGeneticAnalysisDiscreteTraitsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, previousPage func()){
currentPage := func(){setViewCoupleGeneticAnalysisTraitsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, previousPage)}
currentPage := func(){setViewCoupleGeneticAnalysisDiscreteTraitsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, previousPage)}
title := getPageTitleCentered("Viewing Genetic Analysis - Traits")
title := getPageTitleCentered("Viewing Genetic Analysis - Discrete Traits")
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Below is an analysis of the average trait scores for the couple's offspring.")
description := getLabelCentered("Below is an analysis of the average discrete trait outcomes for the couple's offspring.")
getTraitsGrid := func()(*fyne.Container, error){
pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, _, _, _, _, _, _, _, _, err := readGeneticAnalysis.GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject)
if (err != nil){ return nil, err }
mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier)
emptyLabel1 := widget.NewLabel("")
traitNameLabel := getItalicLabelCentered("Trait Name")
offspringOutcomeScoresLabel := getItalicLabelCentered("Offspring Outcome Scores")
emptyLabel2 := widget.NewLabel("")
predictedProbabilitiesLabel := getItalicLabelCentered("Predicted Probabilities")
quantityOfLabel := getItalicLabelCentered("Quantity Of")
lociKnownLabel := getItalicLabelCentered("Loci Known")
emptyLabel3 := widget.NewLabel("")
conflictExistsLabel := getItalicLabelCentered("Conflict Exists?")
emptyLabel := widget.NewLabel("")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
traitNameColumn := container.NewVBox(traitNameLabel, widget.NewSeparator())
offspringOutcomeScoresColumn := container.NewVBox(offspringOutcomeScoresLabel, widget.NewSeparator())
conflictExistsColumn := container.NewVBox(conflictExistsLabel, widget.NewSeparator())
viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator())
traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator())
predictedProbabilitiesColumn := container.NewVBox(emptyLabel2, predictedProbabilitiesLabel, widget.NewSeparator())
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociKnownLabel, widget.NewSeparator())
conflictExistsColumn := container.NewVBox(emptyLabel3, conflictExistsLabel, widget.NewSeparator())
viewDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return nil, err }
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
continue
}
traitLociList := traitObject.LociList
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
if (len(traitLociList) == 0 && len(traitRulesList) == 0){
// This trait does not have any rules
// We cannot analyze it yet
// We will add neural network prediction so we can predict these traits
continue
}
mainGenomePairIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier)
traitName := traitObject.TraitName
offspringOutcomeScoresKnown, offspringAverageOutcomeScoresMap, _, conflictExists, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, mainGenomePairIdentifier)
neuralNetworkExists, neuralNetworkAnalysisExists, offspringOutcomeProbabilitiesMap_NeuralNetwork, _, quantityOfLociKnown_NeuralNetwork, _, anyRulesExist, rulesAnalysisExists, offspringOutcomeProbabilitiesMap_Rules, _, _, quantityOfLociKnown_Rules, conflictExists, err := readGeneticAnalysis.GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, mainGenomePairIdentifier)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false && anyRulesExist == false){
// We cannot analyze this trait
continue
}
// We add all of the columns except for the trait outcomes column, which may be multiple rows high
getQuantityOfLociKnown := func()int{
if (neuralNetworkExists == true){
return quantityOfLociKnown_NeuralNetwork
}
return quantityOfLociKnown_Rules
}
traitNameText := getBoldLabelCentered(traitName)
quantityOfLociKnown := getQuantityOfLociKnown()
getTotalQuantityOfLoci := func()int{
if (neuralNetworkExists == true){
totalQuantityOfLoci := len(traitLociList)
return totalQuantityOfLoci
}
traitLociList_Rules := traitObject.LociList_Rules
totalQuantityOfLoci := len(traitLociList_Rules)
return totalQuantityOfLoci
}
totalQuantityOfLoci := getTotalQuantityOfLoci()
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
// We add each row except for the outcome rows
// The outcome grid cell can be multiple rows tall
traitNameLabel := getBoldLabelCentered(traitName)
conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists)
conflictExistsLabel := getBoldLabelCentered(conflictExistsString)
viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewCoupleGeneticAnalysisTraitDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, currentPage)
setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, currentPage)
}))
traitNameColumn.Add(traitNameText)
traitNameColumn.Add(traitNameLabel)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
conflictExistsColumn.Add(conflictExistsLabel)
viewButtonsColumn.Add(viewDetailsButton)
viewDetailsButtonsColumn.Add(viewDetailsButton)
if (offspringOutcomeScoresKnown == false){
// Outputs:
// -bool: Outcome probabilities exist
// -map[string]int: Outcome Name -> Probability of outcome (0-100)
getOutcomeProbabilitiesMap := func()(bool, map[string]int){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
if (neuralNetworkExists == true){
offspringOutcomeScoresColumn.Add(unknownLabel)
} else {
outcomeNamesList := helpers.GetListOfMapKeys(offspringAverageOutcomeScoresMap)
// We have to sort outcome names so they always show up in the same order
helpers.SortStringListToUnicodeOrder(outcomeNamesList)
for index, outcomeName := range outcomeNamesList{
outcomeScore, exists := offspringAverageOutcomeScoresMap[outcomeName]
if (exists == false){
return nil, errors.New("Outcome name not found in outcomeScoresMap after being found already.")
if (neuralNetworkAnalysisExists == false){
return false, nil
}
outcomeScoreString := helpers.ConvertFloat64ToStringRounded(outcomeScore, 2)
return true, offspringOutcomeProbabilitiesMap_NeuralNetwork
}
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
offspringOutcomeScoresColumn.Add(outcomeRow)
//anyRulesExist must be true
if (rulesAnalysisExists == false){
return false, nil
}
return true, offspringOutcomeProbabilitiesMap_Rules
}
if (index > 0){
outcomeProbabilitiesExist, outcomeProbabilitiesMap := getOutcomeProbabilitiesMap()
if (outcomeProbabilitiesExist == false){
unknownLabel := getItalicLabelCentered("Unknown")
predictedProbabilitiesColumn.Add(unknownLabel)
} else {
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
outcomeNamesList := traitObject.OutcomesList
traitNameColumn.Add(emptyLabelA)
conflictExistsColumn.Add(emptyLabelB)
viewButtonsColumn.Add(emptyLabelC)
outcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(outcomeNamesList)
addedItems := 0
for _, outcomeName := range outcomeNamesListSorted{
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
continue
}
if (outcomeProbability == 0){
continue
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
outcomeRowLabel := getBoldLabel(outcomeName + ": " + outcomeProbabilityString + "%")
predictedProbabilitiesColumn.Add(outcomeRowLabel)
addedItems += 1
if (addedItems != 1){
// We have to add whitespace to the other columns
traitNameColumn.Add(widget.NewLabel(""))
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
conflictExistsColumn.Add(widget.NewLabel(""))
viewDetailsButtonsColumn.Add(widget.NewLabel(""))
}
}
}
traitNameColumn.Add(widget.NewSeparator())
offspringOutcomeScoresColumn.Add(widget.NewSeparator())
predictedProbabilitiesColumn.Add(widget.NewSeparator())
quantityOfLociKnownColumn.Add(widget.NewSeparator())
conflictExistsColumn.Add(widget.NewSeparator())
viewButtonsColumn.Add(widget.NewSeparator())
viewDetailsButtonsColumn.Add(widget.NewSeparator())
}
offspringOutcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitOutcomeScoresExplainerPage(window, currentPage)
predictedProbabilitiesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
offspringOutcomeScoresColumn.Add(offspringOutcomeScoresHelpButton)
predictedProbabilitiesColumn.Add(predictedProbabilitiesHelpButton)
traitsGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, offspringOutcomeScoresColumn)
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
traitsGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, predictedProbabilitiesColumn, quantityOfLociKnownColumn)
if (secondGenomePairExists == true){
@ -1985,7 +2076,7 @@ func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, person1Name stri
traitsGrid.Add(conflictExistsColumn)
}
traitsGrid.Add(viewButtonsColumn)
traitsGrid.Add(viewDetailsButtonsColumn)
traitsGrid.Add(layout.NewSpacer())
return traitsGrid, nil
@ -2003,10 +2094,9 @@ func setViewCoupleGeneticAnalysisTraitsPage(window fyne.Window, person1Name stri
}
func setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, previousPage func()){
func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, previousPage func()){
currentPage := func(){setViewCoupleGeneticAnalysisTraitDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, previousPage)}
currentPage := func(){setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, previousPage)}
title := getPageTitleCentered("Viewing Couple Analysis - " + traitName)
@ -2043,17 +2133,32 @@ func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, person1Nam
})
traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, traitNameInfoButton, layout.NewSpacer())
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
offspringOutcomeScoresLabel := getItalicLabelCentered("Offspring Outcome Scores")
emptyLabel1 := widget.NewLabel("")
emptyLabel2 := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
emptyLabel3 := widget.NewLabel("")
genomePairLabel := getItalicLabelCentered("Genome Pair")
viewGenomePairButtonsColumn := container.NewVBox(emptyLabelA, widget.NewSeparator())
pairNameColumn := container.NewVBox(emptyLabelB, widget.NewSeparator())
offspringOutcomeScoresColumn := container.NewVBox(offspringOutcomeScoresLabel, widget.NewSeparator())
viewOffspringRulesButtonsColumn := container.NewVBox(emptyLabelC, widget.NewSeparator())
emptyLabel4 := widget.NewLabel("")
predictedProbabilitiesLabel := getItalicLabelCentered("Predicted Probabilities")
predictionLabel := getItalicLabelCentered("Prediction")
confidenceLabel := getItalicLabelCentered("Confidence")
quantityOfLabel := getItalicLabelCentered("Quantity Of")
rulesTestedLabel := getItalicLabelCentered("Rules Tested")
emptyLabel5 := widget.NewLabel("")
emptyLabel6 := widget.NewLabel("")
viewGenomePairButtonsColumn := container.NewVBox(emptyLabel1, emptyLabel2, widget.NewSeparator())
pairNameColumn := container.NewVBox(emptyLabel3, genomePairLabel, widget.NewSeparator())
predictedProbabilitiesColumn := container.NewVBox(emptyLabel4, predictedProbabilitiesLabel, widget.NewSeparator())
neuralNetworkPredictionConfidenceColumn := container.NewVBox(predictionLabel, confidenceLabel, widget.NewSeparator())
quantityOfRulesTestedColumn := container.NewVBox(quantityOfLabel, rulesTestedLabel, widget.NewSeparator())
viewDetailsButtonsColumn := container.NewVBox(emptyLabel5, emptyLabel6, widget.NewSeparator())
addGenomePairRow := func(genomePairName string, person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
@ -2062,65 +2167,143 @@ func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, person1Nam
genomePairNameLabel := getBoldLabelCentered(genomePairName)
viewGenomePairButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage)
setViewCoupleGeneticAnalysisDiscreteTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage)
})
viewOffspringRulesButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewCoupleTraitRulesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage)
viewAnalysisDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
if (neuralNetworkExists == true){
//TODO
showUnderConstructionDialog(window)
} else {
setViewCoupleDiscreteTraitRulesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage)
}
})
// We add all of the columns except for the trait rule column, which may be multiple rows high
viewGenomePairButtonsColumn.Add(viewGenomePairButton)
pairNameColumn.Add(genomePairNameLabel)
viewOffspringRulesButtonsColumn.Add(viewOffspringRulesButton)
viewDetailsButtonsColumn.Add(viewAnalysisDetailsButton)
offspringOutcomeScoresKnown, offspringOutcomeScoresMap, _, _, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return err }
if (offspringOutcomeScoresKnown == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
return errors.New("setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage called with non-discrete trait: " + traitName)
}
offspringOutcomeScoresColumn.Add(unknownLabel)
neuralNetworkExists, neuralNetworkAnalysisExists, offspringOutcomeProbabilitiesMap_NeuralNetwork, neuralNetworkPredictionConfidence, _, _, anyRulesExist, rulesAnalysisExists, offspringOutcomeProbabilitiesMap_Rules, _, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
if (err != nil) { return err }
if (neuralNetworkExists == false && anyRulesExist == false){
return errors.New("setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage called with trait that is not analyzable.")
}
// First we add analysis details to their respective column
if (neuralNetworkExists == true){
if (neuralNetworkAnalysisExists == false){
neuralNetworkPredictionConfidenceColumn.Add(getLabelCentered("-"))
} else {
predictionConfidenceString := helpers.ConvertIntToString(neuralNetworkPredictionConfidence)
predictionConfidenceLabel := getBoldLabelCentered(predictionConfidenceString + "%")
neuralNetworkPredictionConfidenceColumn.Add(predictionConfidenceLabel)
}
} else {
if (anyRulesExist == false){
return errors.New("setViewCoupleGeneticAnalysisDiscreteTraitDetailsPage called with analysis which is missing ")
}
outcomeNamesList := helpers.GetListOfMapKeys(offspringOutcomeScoresMap)
traitRulesList := traitObject.RulesList
// We have to sort the outcome names so they always show up in the same order
helpers.SortStringListToUnicodeOrder(outcomeNamesList)
totalNumberOfRules := len(traitRulesList)
for index, outcomeName := range outcomeNamesList{
totalNumberOfRulesString := helpers.ConvertIntToString(totalNumberOfRules)
outcomeScore, exists := offspringOutcomeScoresMap[outcomeName]
if (exists == false){
return errors.New("Outcome name not found in outcome scores map after being found already.")
quantityOfRulesTestedString := helpers.ConvertIntToString(quantityOfRulesTested)
quantityOfRulesTestedFormatted := quantityOfRulesTestedString + "/" + totalNumberOfRulesString
quantityOfRulesTestedLabel := getBoldLabelCentered(quantityOfRulesTestedFormatted)
quantityOfRulesTestedColumn.Add(quantityOfRulesTestedLabel)
}
// Now we add the outcome probabilities cell
// Outputs:
// -bool: Outcome probabilities exist
// -map[string]int: Outcome Name -> Probability of outcome (0-100)
getOutcomeProbabilitiesMap := func()(bool, map[string]int){
if (neuralNetworkExists == true){
if (neuralNetworkAnalysisExists == false){
return false, nil
}
outcomeScoreString := helpers.ConvertFloat64ToStringRounded(outcomeScore, 2)
return true, offspringOutcomeProbabilitiesMap_NeuralNetwork
}
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
offspringOutcomeScoresColumn.Add(outcomeRow)
//anyRulesExist must be true
if (rulesAnalysisExists == false){
return false, nil
}
return true, offspringOutcomeProbabilitiesMap_Rules
}
if (index > 0){
outcomeProbabilitiesExist, outcomeProbabilitiesMap := getOutcomeProbabilitiesMap()
if (outcomeProbabilitiesExist == false){
unknownLabel := getItalicLabelCentered("Unknown")
predictedProbabilitiesColumn.Add(unknownLabel)
} else {
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
outcomeNamesList := traitObject.OutcomesList
pairNameColumn.Add(emptyLabelA)
viewGenomePairButtonsColumn.Add(emptyLabelB)
viewOffspringRulesButtonsColumn.Add(emptyLabelC)
outcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(outcomeNamesList)
addedItems := 0
for _, outcomeName := range outcomeNamesListSorted{
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
continue
}
if (outcomeProbability == 0){
continue
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
outcomeRowLabel := getBoldLabel(outcomeName + ": " + outcomeProbabilityString + "%")
predictedProbabilitiesColumn.Add(outcomeRowLabel)
addedItems += 1
if (addedItems > 1){
// We have to add whitespace to the other columns
viewGenomePairButtonsColumn.Add(widget.NewLabel(""))
pairNameColumn.Add(widget.NewLabel(""))
neuralNetworkPredictionConfidenceColumn.Add(widget.NewLabel(""))
quantityOfRulesTestedColumn.Add(widget.NewLabel(""))
viewDetailsButtonsColumn.Add(widget.NewLabel(""))
}
}
}
viewGenomePairButtonsColumn.Add(widget.NewSeparator())
pairNameColumn.Add(widget.NewSeparator())
offspringOutcomeScoresColumn.Add(widget.NewSeparator())
viewOffspringRulesButtonsColumn.Add(widget.NewSeparator())
predictedProbabilitiesColumn.Add(widget.NewSeparator())
neuralNetworkPredictionConfidenceColumn.Add(widget.NewSeparator())
quantityOfRulesTestedColumn.Add(widget.NewSeparator())
viewDetailsButtonsColumn.Add(widget.NewSeparator())
return nil
}
@ -2139,13 +2322,39 @@ func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, person1Nam
}
}
offspringOutcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitOutcomeScoresExplainerPage(window, currentPage)
predictedProbabilitiesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
offspringOutcomeScoresColumn.Add(offspringOutcomeScoresHelpButton)
predictedProbabilitiesColumn.Add(predictedProbabilitiesHelpButton)
genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairButtonsColumn, pairNameColumn, offspringOutcomeScoresColumn, viewOffspringRulesButtonsColumn, layout.NewSpacer())
if (neuralNetworkExists == true){
neuralNetworkPredictionConfidenceHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringDiscreteTraitNeuralNetworkPredictionExplainerPage(window, currentPage)
})
neuralNetworkPredictionConfidenceColumn.Add(neuralNetworkPredictionConfidenceHelpButton)
} else {
quantityOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
})
quantityOfRulesTestedColumn.Add(quantityOfRulesTestedHelpButton)
}
genomesContainer := container.NewHBox(layout.NewSpacer(), viewGenomePairButtonsColumn, pairNameColumn, predictedProbabilitiesColumn)
if (neuralNetworkExists == true){
genomesContainer.Add(neuralNetworkPredictionConfidenceColumn)
} else {
genomesContainer.Add(quantityOfRulesTestedColumn)
}
genomesContainer.Add(viewDetailsButtonsColumn)
genomesContainer.Add(layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), traitNameRow, widget.NewSeparator(), genomesContainer)
@ -2153,9 +2362,9 @@ func setViewCoupleGeneticAnalysisTraitDetailsPage(window fyne.Window, person1Nam
}
func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){
func setViewCoupleGeneticAnalysisDiscreteTraitGenomePairDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){
currentPage := func(){setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, previousPage)}
currentPage := func(){setViewCoupleGeneticAnalysisDiscreteTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, previousPage)}
title := getPageTitleCentered("Viewing Couple Genome Pair Info")
@ -2177,26 +2386,30 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window,
})
genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, genomePairHelpButton, layout.NewSpacer())
emptyLabelA := widget.NewLabel("")
emptyLabel1 := widget.NewLabel("")
personNameLabel := getItalicLabelCentered("Person Name")
emptyLabelB := widget.NewLabel("")
emptyLabel2 := widget.NewLabel("")
genomeNameLabel := getItalicLabelCentered("Genome Name")
emptyLabelC := widget.NewLabel("")
outcomeScoresLabel := getItalicLabelCentered("Outcome Scores")
emptyLabel3 := widget.NewLabel("")
predictedOutcomeLabel := getItalicLabelCentered("Predicted Outcome")
numberOfLabel := getItalicLabelCentered("Number Of")
quantityOfLabel1 := getItalicLabelCentered("Quantity Of")
lociKnownLabel := getItalicLabelCentered("Loci Known")
quantityOfLabel2 := getItalicLabelCentered("Quantity Of")
rulesTestedLabel := getItalicLabelCentered("Rules Tested")
emptyLabelD := widget.NewLabel("")
emptyLabelE := widget.NewLabel("")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
personNameColumn := container.NewVBox(emptyLabelA, personNameLabel, widget.NewSeparator())
genomeNameColumn := container.NewVBox(emptyLabelB, genomeNameLabel, widget.NewSeparator())
outcomeScoresColumn := container.NewVBox(emptyLabelC, outcomeScoresLabel, widget.NewSeparator())
numberOfRulesTestedColumn := container.NewVBox(numberOfLabel, rulesTestedLabel, widget.NewSeparator())
viewGenomeButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator())
personNameColumn := container.NewVBox(emptyLabel1, personNameLabel, widget.NewSeparator())
genomeNameColumn := container.NewVBox(emptyLabel2, genomeNameLabel, widget.NewSeparator())
predictedOutcomeColumn := container.NewVBox(emptyLabel3, predictedOutcomeLabel, widget.NewSeparator())
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel1, lociKnownLabel, widget.NewSeparator())
quantityOfRulesTestedColumn := container.NewVBox(quantityOfLabel2, rulesTestedLabel, widget.NewSeparator())
viewGenomeButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
addGenomeRow := func(isPerson1 bool, personName string, inputGenomeIdentifier [16]byte)error{
@ -2238,68 +2451,103 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window,
genomeNameLabel := getBoldLabelCentered(genomeName)
// We add all of the columns except for the trait rule column, which may be multiple rows high
_, anyTraitRuleTested, outcomeScoresMap, numberOfRulesTested, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, personAnalysisGenomeIdentifier)
neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, _, neuralNetworkQuantityOfLociTested, _, _, rulesAnalysisExists, _, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, personAnalysisGenomeIdentifier)
if (err != nil) { return err }
numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested)
numberOfRulesTestedText := getBoldLabelCentered(numberOfRulesTestedString)
viewGenomeButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewPersonGenomeTraitRulesPage(window, personAnalysisObject, traitName, personAnalysisGenomeIdentifier, genomeName, currentPage)
})
personNameColumn.Add(personNameLabel)
genomeNameColumn.Add(genomeNameLabel)
numberOfRulesTestedColumn.Add(numberOfRulesTestedText)
viewGenomeButtonsColumn.Add(viewGenomeButton)
if (anyTraitRuleTested == false){
//Outputs:
// -bool: Outcome is known
// -string: Prediction outcome
getPredictedOutcome := func()(bool, string){
if (neuralNetworkExists == true){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
if (neuralNetworkAnalysisExists == false){
return false, ""
}
outcomeScoresColumn.Add(unknownLabel)
return true, neuralNetworkPredictedOutcome
}
// Analysis must be rule-based
if (rulesAnalysisExists == false || rulesPredictedOutcomeExists == false){
return false, ""
}
return true, rulesPredictedOutcome
}
predictedOutcomeExists, predictedOutcome := getPredictedOutcome()
if (predictedOutcomeExists == false){
unknownLabel := getItalicLabelCentered(translate("Unknown"))
predictedOutcomeColumn.Add(unknownLabel)
} else {
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
predictedOutcomeColumn.Add(predictedOutcomeLabel)
}
// Now we add outcome details
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return err }
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
return errors.New("setViewCoupleGeneticAnalysisDiscreteTraitGenomePairDetailsPage called with non-discrete trait: " + traitName)
}
if (neuralNetworkExists == true){
traitLociList := traitObject.LociList
totalQuantityOfLoci := len(traitLociList)
quantityOfLociKnownString := helpers.ConvertIntToString(neuralNetworkQuantityOfLociTested)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
} else {
outcomeNamesList := helpers.GetListOfMapKeys(outcomeScoresMap)
// Analysis is rule-based
// We have to sort the outcome names so they always show up in the same order
helpers.SortStringListToUnicodeOrder(outcomeNamesList)
traitRulesList := traitObject.RulesList
for index, outcomeName := range outcomeNamesList{
totalQuantityOfRules := len(traitRulesList)
outcomeScore, exists := outcomeScoresMap[outcomeName]
if (exists == false){
return errors.New("Outcome name not found in outcome scores map after being found already.")
}
quantityOfRulesTestedString := helpers.ConvertIntToString(quantityOfRulesTested)
totalQuantityOfRulesString := helpers.ConvertIntToString(totalQuantityOfRules)
outcomeScoreString := helpers.ConvertIntToString(outcomeScore)
quantityOfRulesTestedFormatted := quantityOfRulesTestedString + "/" + totalQuantityOfRulesString
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
outcomeScoresColumn.Add(outcomeRow)
quantityOfRulesTestedLabel := getBoldLabelCentered(quantityOfRulesTestedFormatted)
if (index > 0){
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
emptyLabelD := widget.NewLabel("")
personNameColumn.Add(emptyLabelA)
genomeNameColumn.Add(emptyLabelB)
numberOfRulesTestedColumn.Add(emptyLabelC)
viewGenomeButtonsColumn.Add(emptyLabelD)
}
}
quantityOfRulesTestedColumn.Add(quantityOfRulesTestedLabel)
}
viewGenomeButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
if (neuralNetworkExists == true){
//TODO
showUnderConstructionDialog(window)
} else {
setViewPersonGenomeDiscreteTraitRulesPage(window, personAnalysisObject, traitName, personAnalysisGenomeIdentifier, genomeName, currentPage)
}
})
viewGenomeButtonsColumn.Add(viewGenomeButton)
personNameColumn.Add(widget.NewSeparator())
genomeNameColumn.Add(widget.NewSeparator())
outcomeScoresColumn.Add(widget.NewSeparator())
numberOfRulesTestedColumn.Add(widget.NewSeparator())
predictedOutcomeColumn.Add(widget.NewSeparator())
quantityOfLociKnownColumn.Add(widget.NewSeparator())
quantityOfRulesTestedColumn.Add(widget.NewSeparator())
viewGenomeButtonsColumn.Add(widget.NewSeparator())
return nil
@ -2318,19 +2566,44 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window,
return
}
outcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitOutcomeScoresExplainerPage(window, currentPage)
neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
if (neuralNetworkExists == true){
setDiscreteTraitNeuralNetworkPredictionExplainerPage(window, currentPage)
} else {
setOffspringDiscreteTraitNeuralNetworkPredictionExplainerPage(window, currentPage)
}
})
outcomeScoresColumn.Add(outcomeScoresHelpButton)
predictedOutcomeColumn.Add(predictedOutcomeHelpButton)
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
numberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitNumberOfRulesTestedExplainerPage(window, currentPage)
setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
})
numberOfRulesTestedColumn.Add(numberOfRulesTestedHelpButton)
quantityOfRulesTestedColumn.Add(numberOfRulesTestedHelpButton)
genomesGrid := container.NewHBox(layout.NewSpacer(), personNameColumn, genomeNameColumn, outcomeScoresColumn, numberOfRulesTestedColumn, viewGenomeButtonsColumn, layout.NewSpacer())
genomesGrid := container.NewHBox(layout.NewSpacer(), personNameColumn, genomeNameColumn, predictedOutcomeColumn)
if (neuralNetworkExists == true){
genomesGrid.Add(quantityOfLociKnownColumn)
} else {
genomesGrid.Add(quantityOfRulesTestedColumn)
}
genomesGrid.Add(viewGenomeButtonsColumn)
genomesGrid.Add(layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), traitNameRow, widget.NewSeparator(), genomePairRow, widget.NewSeparator(), genomesGrid)
@ -2338,13 +2611,12 @@ func setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window fyne.Window,
}
// This function provides a page to view the couple offspring rule probabilities for a particular genome pair
func setViewCoupleTraitRulesPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){
func setViewCoupleDiscreteTraitRulesPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte, genomePairName string, previousPage func()){
setLoadingScreen(window, "Loading Trait Rules", "Loading trait rules...")
currentPage := func(){setViewCoupleTraitRulesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, previousPage)}
currentPage := func(){setViewCoupleDiscreteTraitRulesPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, previousPage)}
title := getPageTitleCentered("View Offspring Trait Rules - " + traitName)
@ -2352,25 +2624,33 @@ func setViewCoupleTraitRulesPage(window fyne.Window, person1Name string, person2
description1 := widget.NewLabel("Below are the trait rule probabilities for offspring from this genome pair.")
traitRulesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitRulesExplainerPage(window, currentPage)
setOffspringDiscreteTraitRulesExplainerPage(window, currentPage)
})
description1Row := container.NewHBox(layout.NewSpacer(), description1, traitRulesHelpButton, layout.NewSpacer())
genomePairLabel := widget.NewLabel("Genome Pair:")
genomePairNameLabel := getBoldLabel(genomePairName)
viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage)
setViewCoupleGeneticAnalysisDiscreteTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage)
})
genomePairRow := container.NewHBox(layout.NewSpacer(), genomePairLabel, genomePairNameLabel, viewGenomePairInfoButton, layout.NewSpacer())
_, _, numberOfRulesTested, _, err := readGeneticAnalysis.GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
neuralNetworkExists, _, _, _, _, _, anyRulesExist, rulesAnalysisExists, _, _, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (neuralNetworkExists == true){
setErrorEncounteredPage(window, errors.New("setViewCoupleTraitRulesPage called when neural network analysis for trait exists."), previousPage)
return
}
if (anyRulesExist == false){
setErrorEncounteredPage(window, errors.New("GetOffspringTraitInfoFromGeneticAnalysis claiming that no analysis method exists for triat: " + traitName), previousPage)
return
}
numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested)
quantityOfRulesTestedString := helpers.ConvertIntToString(quantityOfRulesTested)
traitRulesMap, err := traits.GetTraitRulesMap(traitName)
if (err != nil){
@ -2382,9 +2662,9 @@ func setViewCoupleTraitRulesPage(window fyne.Window, person1Name string, person2
totalNumberOfRulesString := helpers.ConvertIntToString(totalNumberOfRules)
rulesTestedLabel := widget.NewLabel("Rules Tested:")
rulesTestedText := getBoldLabel(numberOfRulesTestedString + "/" + totalNumberOfRulesString)
rulesTestedText := getBoldLabel(quantityOfRulesTestedString + "/" + totalNumberOfRulesString)
rulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitNumberOfRulesTestedExplainerPage(window, currentPage)
setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
})
rulesTestedRow := container.NewHBox(layout.NewSpacer(), rulesTestedLabel, rulesTestedText, rulesTestedHelpButton, layout.NewSpacer())
@ -2415,25 +2695,33 @@ func setViewCoupleTraitRulesPage(window fyne.Window, person1Name string, person2
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
offspringRuleProbabilityKnown, _, offspringProbabilityOfPassingRuleFormatted, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
if (err != nil) { return err }
getProbabilityOfPassingRuleText := func()(string, error){
viewRuleDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, ruleIdentifier, currentPage)
})
if (rulesAnalysisExists == false){
// No rules were tested
result := translate("Unknown")
return result, nil
}
getProbabilityOfPassingRuleText := func()string{
offspringRuleProbabilityKnown, _, offspringProbabilityOfPassingRuleFormatted, err := readGeneticAnalysis.GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
if (err != nil) { return "", err }
if (offspringRuleProbabilityKnown == false){
result := translate("Unknown")
return result
return result, nil
}
return offspringProbabilityOfPassingRuleFormatted
return offspringProbabilityOfPassingRuleFormatted, nil
}
probabilityOfPassingRuleText := getProbabilityOfPassingRuleText()
probabilityOfPassingRuleText, err := getProbabilityOfPassingRuleText()
if (err != nil) { return err }
probabilityOfPassingRuleTextLabel := getBoldLabelCentered(probabilityOfPassingRuleText)
viewRuleDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewCoupleGeneticAnalysisDiscreteTraitRuleDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, ruleIdentifier, currentPage)
})
// We add all of the columns except for the rule effects column
// We do this because the rule effects column may be multiple rows tall
@ -2504,7 +2792,7 @@ func setViewCoupleTraitRulesPage(window fyne.Window, person1Name string, person2
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return nil, err }
offspringRuleProbabilityKnown, _, _, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
offspringRuleProbabilityKnown, _, _, err := readGeneticAnalysis.GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
if (err != nil) { return nil, err }
if (offspringRuleProbabilityKnown == true){
rulesWithKnownProbabilityList = append(rulesWithKnownProbabilityList, ruleIdentifierHex)
@ -2531,7 +2819,7 @@ func setViewCoupleTraitRulesPage(window fyne.Window, person1Name string, person2
}
ruleEffectsHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
})
ruleEffectsColumn.Add(ruleEffectsHelpButton)
@ -2560,9 +2848,9 @@ func setViewCoupleTraitRulesPage(window fyne.Window, person1Name string, person2
// This function implements a page to view the details of a specific rule from a genetic analysis
// It will show the rule details for all of the couple's genome pairs
func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){
func setViewCoupleGeneticAnalysisDiscreteTraitRuleDetailsPage(window fyne.Window, person1Name string, person2Name string, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){
currentPage := func(){setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, ruleIdentifier, previousPage)}
currentPage := func(){setViewCoupleGeneticAnalysisDiscreteTraitRuleDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, ruleIdentifier, previousPage)}
title := getPageTitleCentered("Trait Rule Details - " + traitName)
@ -2575,7 +2863,7 @@ func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, person
ruleIdentifierLabel := widget.NewLabel("Rule Identifier:")
ruleIdentifierText := getBoldLabel(ruleIdentifierHex)
ruleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
})
ruleIdentifierRow := container.NewHBox(layout.NewSpacer(), ruleIdentifierLabel, ruleIdentifierText, ruleInfoButton, layout.NewSpacer())
@ -2596,7 +2884,7 @@ func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, person
addGenomePairRow := func(genomePairName string, genomePairIdentifier [32]byte)error{
offspringRuleProbabilityKnown, _, offspringProbabilityOfPassingRuleFormatted, err := readGeneticAnalysis.GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
offspringRuleProbabilityKnown, _, offspringProbabilityOfPassingRuleFormatted, err := readGeneticAnalysis.GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
if (err != nil) { return err }
getOffspringProbabilityOfPassingRuleText := func()string{
@ -2612,7 +2900,7 @@ func setViewCoupleGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, person
offspringProbabilityOfPassingRuleText := getOffspringProbabilityOfPassingRuleText()
viewGenomePairInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewCoupleGeneticAnalysisTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage)
setViewCoupleGeneticAnalysisDiscreteTraitGenomePairDetailsPage(window, person1Name, person2Name, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, traitName, genomePairIdentifier, genomePairName, currentPage)
})
genomePairNameLabel := getBoldLabelCentered(genomePairName)

View file

@ -13,6 +13,7 @@ import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/widget"
import "seekia/resources/geneticPredictionModels"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
import "seekia/resources/geneticReferences/traits"
@ -70,11 +71,15 @@ func setViewPersonGeneticAnalysisPage(window fyne.Window, personIdentifier strin
polygenicDiseasesButton := widget.NewButton("Polygenic Diseases", func(){
setViewPersonGeneticAnalysisPolygenicDiseasesPage(window, personIdentifier, analysisObject, currentPage)
})
traitsButton := widget.NewButton("Traits", func(){
setViewPersonGeneticAnalysisTraitsPage(window, personIdentifier, analysisObject, currentPage)
discreteTraitsButton := widget.NewButton("Discrete Traits", func(){
setViewPersonGeneticAnalysisDiscreteTraitsPage(window, personIdentifier, analysisObject, currentPage)
})
numericTraitsButton := widget.NewButton("Numeric Traits", func(){
//TODO
showUnderConstructionDialog(window)
})
categoryButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, generalButton, monogenicDiseasesButton, polygenicDiseasesButton, traitsButton))
categoryButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, generalButton, monogenicDiseasesButton, polygenicDiseasesButton, discreteTraitsButton, numericTraitsButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), warningLabel1, warningLabel2, widget.NewSeparator(), personNameRow, numberOfAnalyzedGenomesRow, widget.NewSeparator(), categoryButtonsGrid)
@ -94,7 +99,7 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy
getMonogenicDiseasesContainer := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil){ return nil, err }
// Outputs:
@ -241,7 +246,7 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window,
backButton := getBackButtonCentered(previousPage)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -280,8 +285,6 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window,
totalNumberOfVariants := len(diseaseVariantsMap)
totalNumberOfVariantsString := helpers.ConvertIntToString(totalNumberOfVariants)
emptyLabelA := widget.NewLabel("")
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -736,7 +739,7 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne.
getGenomesHaveVariantGrid := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
if (err != nil) { return nil, err }
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -894,7 +897,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso
getPolygenicDiseasesContainer := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil){ return nil, err }
// Outputs:
@ -935,7 +938,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso
diseaseNameText := getBoldLabelCentered(diseaseName)
personRiskScoreKnown, _, personRiskScoreFormatted, _, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier)
personRiskScoreKnown, _, personRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier)
if (err != nil) { return nil, err }
getPersonRiskScoreLabelText := func()string{
@ -1015,7 +1018,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
backButton := getBackButtonCentered(previousPage)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -1054,7 +1057,6 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
totalNumberOfLoci := len(diseaseLociMap)
totalNumberOfLociString := helpers.ConvertIntToString(totalNumberOfLoci)
emptyLabelA := widget.NewLabel("")
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -1096,7 +1098,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
genomeNameCell := getGenomeNameCell()
diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, _, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier)
diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier)
if (err != nil) { return err }
getRiskScoreLabelText := func()string{
@ -1622,7 +1624,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
getGenomesLocusInfoGrid := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
if (err != nil) { return nil, err }
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -1765,19 +1767,19 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
}
func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, previousPage func()){
func setViewPersonGeneticAnalysisDiscreteTraitsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, previousPage func()){
currentPage := func(){setViewPersonGeneticAnalysisTraitsPage(window, personIdentifier, analysisObject, previousPage)}
currentPage := func(){setViewPersonGeneticAnalysisDiscreteTraitsPage(window, personIdentifier, analysisObject, previousPage)}
title := getPageTitleCentered("Viewing Genetic Analysis - Traits")
title := getPageTitleCentered("Viewing Genetic Analysis - Discrete Traits")
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Below is an analysis of the traits for this person's genome.")
description := getLabelCentered("Below is an analysis of the discrete traits for this person's genome.")
getTraitsContainer := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil){ return nil, err }
// Outputs:
@ -1798,102 +1800,154 @@ func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier
mainGenomeIdentifier, err := getMainGenomeIdentifier()
if (err != nil){ return nil, err }
emptyLabel1 := widget.NewLabel("")
traitNameLabel := getItalicLabelCentered("Trait Name")
outcomeScoresLabel := getItalicLabelCentered("Outcome Scores")
emptyLabel2 := widget.NewLabel("")
predictedOutcomeLabel := getItalicLabelCentered("Predicted Outcome")
quantityOfLabel := getItalicLabelCentered("Quantity Of")
lociKnownLabel := getItalicLabelCentered("Loci Known")
emptyLabel3 := widget.NewLabel("")
conflictExistsLabel := getItalicLabelCentered("Conflict Exists?")
emptyLabel := widget.NewLabel("")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
traitNameColumn := container.NewVBox(traitNameLabel, widget.NewSeparator())
outcomeScoresColumn := container.NewVBox(outcomeScoresLabel, widget.NewSeparator())
conflictExistsColumn := container.NewVBox(conflictExistsLabel, widget.NewSeparator())
viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator())
traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator())
predictedOutcomeColumn := container.NewVBox(emptyLabel2, predictedOutcomeLabel, widget.NewSeparator())
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociKnownLabel, widget.NewSeparator())
conflictExistsColumn := container.NewVBox(emptyLabel3, conflictExistsLabel, widget.NewSeparator())
viewButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return nil, err }
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
// This trait does not have any rules
// We cannot analyze it yet
// We will add neural network prediction so we can predict these traits
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
continue
}
traitOutcomeNamesList := traitObject.OutcomesList
_, anyTraitRuleTested, outcomeScoresMap, _, conflictExists, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(analysisObject, traitName, mainGenomeIdentifier)
traitName := traitObject.TraitName
traitLociList := traitObject.LociList
traitRulesList := traitObject.RulesList
if (len(traitLociList) == 0 && len(traitRulesList) == 0){
// This trait does not have any rules or loci to analyze
// We cannot analyze it yet
continue
}
traitNameText := getBoldLabelCentered(traitName)
neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, _, quantityOfLociKnown_NeuralNetwork, _, anyRulesExist, rulesAnalysisExists, _, rulesPredictedOutcomeExists, rulesPredictedOutcome, _, quantityOfLociKnown_Rules, conflictExists, err := readGeneticAnalysis.GetPersonDiscreteTraitInfoFromGeneticAnalysis(analysisObject, traitName, mainGenomeIdentifier)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false && anyRulesExist == false){
// We can't analyze this trait
continue
}
// We add all of the columns except for the trait outcomes column, which may be multiple rows high
getPredictionLabel := func()fyne.Widget{
if (neuralNetworkExists == true){
if (neuralNetworkAnalysisExists == false){
result := getItalicLabel("Unknown")
return result
}
result := getBoldLabel(neuralNetworkPredictedOutcome)
return result
}
// anyRulesExist must be true
if (rulesAnalysisExists == false || rulesPredictedOutcomeExists == false){
result := getItalicLabel("Unknown")
return result
}
result := getBoldLabel(rulesPredictedOutcome)
return result
}
predictionLabel := getPredictionLabel()
predictionLabelCentered := getWidgetCentered(predictionLabel)
getQuantityOfLociKnown := func()int{
if (neuralNetworkExists == true){
return quantityOfLociKnown_NeuralNetwork
}
return quantityOfLociKnown_Rules
}
quantityOfLociKnown := getQuantityOfLociKnown()
getTotalQuantityOfLoci := func()int{
if (neuralNetworkExists == true){
totalQuantityOfLoci := len(traitLociList)
return totalQuantityOfLoci
}
traitLociList_Rules := traitObject.LociList_Rules
totalQuantityOfLoci := len(traitLociList_Rules)
return totalQuantityOfLoci
}
totalQuantityOfLoci := getTotalQuantityOfLoci()
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists)
conflictExistsLabel := getBoldLabelCentered(conflictExistsString)
viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewPersonGeneticAnalysisTraitDetailsPage(window, personIdentifier, analysisObject, traitName, currentPage)
setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window, personIdentifier, analysisObject, traitName, currentPage)
}))
traitNameText := getBoldLabelCentered(traitName)
traitNameColumn.Add(traitNameText)
predictedOutcomeColumn.Add(predictionLabelCentered)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
conflictExistsColumn.Add(conflictExistsLabel)
viewButtonsColumn.Add(viewDetailsButton)
if (anyTraitRuleTested == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
outcomeScoresColumn.Add(unknownLabel)
} else {
// We have to sort outcome names so they always show up in the same order
traitOutcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(traitOutcomeNamesList)
for index, outcomeName := range traitOutcomeNamesListSorted{
outcomeScore, exists := outcomeScoresMap[outcomeName]
if (exists == false){
return nil, errors.New("Outcome name not found in outcomeScoresMap: " + outcomeName)
}
outcomeScoreString := helpers.ConvertIntToString(outcomeScore)
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
outcomeScoresColumn.Add(outcomeRow)
if (index > 0){
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
traitNameColumn.Add(emptyLabelA)
conflictExistsColumn.Add(emptyLabelB)
viewButtonsColumn.Add(emptyLabelC)
}
}
}
traitNameColumn.Add(widget.NewSeparator())
outcomeScoresColumn.Add(widget.NewSeparator())
predictedOutcomeColumn.Add(widget.NewSeparator())
quantityOfLociKnownColumn.Add(widget.NewSeparator())
conflictExistsColumn.Add(widget.NewSeparator())
viewButtonsColumn.Add(widget.NewSeparator())
}
outcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitOutcomeScoresExplainerPage(window, currentPage)
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
outcomeScoresColumn.Add(outcomeScoresHelpButton)
predictedOutcomeColumn.Add(predictedOutcomeHelpButton)
traitsContainer := container.NewHBox(layout.NewSpacer(), traitNameColumn, outcomeScoresColumn)
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
traitsContainer := container.NewHBox(layout.NewSpacer(), traitNameColumn, predictedOutcomeColumn, quantityOfLociKnownColumn)
if (multipleGenomesExist == true){
@ -1923,16 +1977,15 @@ func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier
}
func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, traitName string, previousPage func()){
func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, traitName string, previousPage func()){
currentPage := func(){setViewPersonGeneticAnalysisTraitDetailsPage(window, personIdentifier, analysisObject, traitName, previousPage)}
currentPage := func(){setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window, personIdentifier, analysisObject, traitName, previousPage)}
title := getPageTitleCentered("Viewing Genetic Analysis - " + traitName)
backButton := getBackButtonCentered(previousPage)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -1963,30 +2016,48 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden
})
traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, traitInfoButton, layout.NewSpacer())
neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
getGenomesContainer := func()(*fyne.Container, error){
traitRulesMap, err := traits.GetTraitRulesMap(traitName)
if (err != nil){ return nil, err }
totalNumberOfRules := len(traitRulesMap)
totalNumberOfRulesString := helpers.ConvertIntToString(totalNumberOfRules)
emptyLabelA := widget.NewLabel("")
emptyLabel1 := widget.NewLabel("")
genomeNameLabel := getItalicLabelCentered("Genome Name")
emptyLabelB := widget.NewLabel("")
outcomeScoresLabel := getItalicLabelCentered("Outcome Scores")
emptyLabel2 := widget.NewLabel("")
predictedOutcomeLabel := getItalicLabelCentered("Predicted Outcome")
numberOfLabel := getItalicLabelCentered("Number of")
emptyLabel3 := widget.NewLabel("")
predictionConfidenceLabel := getItalicLabelCentered("Prediction Confidence")
quantityOfLabel := getItalicLabelCentered("Quantity Of")
rulesTestedLabel := getItalicLabelCentered("Rules Tested")
emptyLabelD := widget.NewLabel("")
emptyLabelE := widget.NewLabel("")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
genomeNameColumn := container.NewVBox(emptyLabelA, genomeNameLabel, widget.NewSeparator())
outcomeScoresColumn := container.NewVBox(emptyLabelB, outcomeScoresLabel, widget.NewSeparator())
numberOfRulesTestedColumn := container.NewVBox(numberOfLabel, rulesTestedLabel, widget.NewSeparator())
viewRulesButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator())
genomeNameColumn := container.NewVBox()
predictedOutcomeColumn := container.NewVBox()
neuralNetworkPredictionConfidenceColumn := container.NewVBox()
numberOfRulesTestedColumn := container.NewVBox(quantityOfLabel, rulesTestedLabel, widget.NewSeparator())
viewDetailsButtonsColumn := container.NewVBox()
if (neuralNetworkExists == false){
// We only need an extra header row if the numberOfRulesTested column is shown
genomeNameColumn.Add(emptyLabel1)
predictedOutcomeColumn.Add(emptyLabel2)
neuralNetworkPredictionConfidenceColumn.Add(emptyLabel3)
viewDetailsButtonsColumn.Add(emptyLabel4)
}
genomeNameColumn.Add(genomeNameLabel)
predictedOutcomeColumn.Add(predictedOutcomeLabel)
neuralNetworkPredictionConfidenceColumn.Add(predictionConfidenceLabel)
viewDetailsButtonsColumn.Add(emptyLabel5)
genomeNameColumn.Add(widget.NewSeparator())
predictedOutcomeColumn.Add(widget.NewSeparator())
neuralNetworkPredictionConfidenceColumn.Add(widget.NewSeparator())
viewDetailsButtonsColumn.Add(widget.NewSeparator())
addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{
@ -2008,66 +2079,121 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden
genomeNameCell := getGenomeNameCell()
_, anyTraitRuleTested, outcomeScoresMap, numberOfRulesTested, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(analysisObject, traitName, genomeIdentifier)
neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, _, _, anyRulesExist, rulesAnalysisExists, _, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetPersonDiscreteTraitInfoFromGeneticAnalysis(analysisObject, traitName, genomeIdentifier)
if (err != nil) { return err }
// We add all of the columns except for the trait rule column, which may be multiple rows high
if (neuralNetworkExists == false && anyRulesExist == false){
// This trait is not analyzable
return errors.New("setViewPersonGeneticAnalysisTraitDetailsPage called with trait which cannot be analyzed via neural networks and traits.")
}
genomeNumberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested)
numberOfRulesTestedLabel := getBoldLabelCentered(genomeNumberOfRulesTestedString + "/" + totalNumberOfRulesString)
getPredictedOutcomeLabel := func()fyne.Widget{
viewRulesButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewPersonGenomeTraitRulesPage(window, analysisObject, traitName, genomeIdentifier, genomeName, currentPage)
if (neuralNetworkExists == true){
if (neuralNetworkAnalysisExists == false){
predictedOutcomeLabel := getItalicLabel("Unknown")
return predictedOutcomeLabel
}
predictedOutcomeLabel := getBoldLabel(neuralNetworkPredictedOutcome)
return predictedOutcomeLabel
}
// anyRulesExist must be true
if (rulesAnalysisExists == false || rulesPredictedOutcomeExists == false){
predictedOutcomeLabel := getItalicLabel("Unknown")
return predictedOutcomeLabel
}
predictedOutcomeLabel := getBoldLabel(rulesPredictedOutcome)
return predictedOutcomeLabel
}
predictedOutcomeLabel := getPredictedOutcomeLabel()
predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel)
getNeuralNetworkConfidenceLabel := func()fyne.Widget{
if (neuralNetworkExists == false){
emptyLabel := widget.NewLabel("")
return emptyLabel
}
if (neuralNetworkAnalysisExists == false){
predictedOutcomeConfidenceLabel := getItalicLabel("Unknown")
return predictedOutcomeConfidenceLabel
}
neuralNetworkPredictionConfidenceString := helpers.ConvertIntToString(neuralNetworkPredictionConfidence)
predictedOutcomeConfidenceLabel := getBoldLabel(neuralNetworkPredictionConfidenceString + "%")
return predictedOutcomeConfidenceLabel
}
predictedOutcomeConfidenceLabel := getNeuralNetworkConfidenceLabel()
predictedOutcomeConfidenceLabelCentered := getWidgetCentered(predictedOutcomeConfidenceLabel)
getQuantityOfRulesTestedLabel := func()(fyne.Widget, error){
if (anyRulesExist == false){
emptyLabel := widget.NewLabel("")
return emptyLabel, nil
}
traitRulesMap, err := traits.GetTraitRulesMap(traitName)
if (err != nil){ return nil, err }
totalNumberOfRules := len(traitRulesMap)
totalNumberOfRulesString := helpers.ConvertIntToString(totalNumberOfRules)
if (rulesAnalysisExists == false){
quantityOfRulesTestedLabel := getItalicLabel("0/" + totalNumberOfRulesString)
return quantityOfRulesTestedLabel, nil
}
quantityOfRulesTestedString := helpers.ConvertIntToString(quantityOfRulesTested)
quantityOfRulesTestedLabel := getBoldLabel(quantityOfRulesTestedString + "/" + totalNumberOfRulesString)
return quantityOfRulesTestedLabel, nil
}
quantityOfRulesTestedLabel, err := getQuantityOfRulesTestedLabel()
if (err != nil) { return err }
quantityOfRulesTestedLabelCentered := getWidgetCentered(quantityOfRulesTestedLabel)
viewDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
if (neuralNetworkExists == true){
//TODO
showUnderConstructionDialog(window)
} else {
setViewPersonGenomeDiscreteTraitRulesPage(window, analysisObject, traitName, genomeIdentifier, genomeName, currentPage)
}
})
genomeNameColumn.Add(genomeNameCell)
numberOfRulesTestedColumn.Add(numberOfRulesTestedLabel)
viewRulesButtonsColumn.Add(viewRulesButton)
if (anyTraitRuleTested == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
outcomeScoresColumn.Add(unknownLabel)
} else {
// We have to sort the outcome names so they always show up in the same order
outcomeNamesList := helpers.GetListOfMapKeys(outcomeScoresMap)
helpers.SortStringListToUnicodeOrder(outcomeNamesList)
for index, outcomeName := range outcomeNamesList{
outcomeScore, exists := outcomeScoresMap[outcomeName]
if (exists == false){
return errors.New("Outcome name not found in outcome scores map after being found already: " + outcomeName)
}
outcomeScoreString := helpers.ConvertIntToString(outcomeScore)
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
outcomeScoresColumn.Add(outcomeRow)
if (index > 0){
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
genomeNameColumn.Add(emptyLabelA)
numberOfRulesTestedColumn.Add(emptyLabelB)
viewRulesButtonsColumn.Add(emptyLabelC)
}
}
}
predictedOutcomeColumn.Add(predictedOutcomeLabelCentered)
neuralNetworkPredictionConfidenceColumn.Add(predictedOutcomeConfidenceLabelCentered)
numberOfRulesTestedColumn.Add(quantityOfRulesTestedLabelCentered)
viewDetailsButtonsColumn.Add(viewDetailsButton)
genomeNameColumn.Add(widget.NewSeparator())
outcomeScoresColumn.Add(widget.NewSeparator())
predictedOutcomeColumn.Add(widget.NewSeparator())
neuralNetworkPredictionConfidenceColumn.Add(widget.NewSeparator())
numberOfRulesTestedColumn.Add(widget.NewSeparator())
viewRulesButtonsColumn.Add(widget.NewSeparator())
viewDetailsButtonsColumn.Add(widget.NewSeparator())
return nil
}
@ -2112,17 +2238,41 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden
if (err != nil){ return nil, err }
}
outcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitOutcomeScoresExplainerPage(window, currentPage)
if (neuralNetworkExists == true){
neuralNetworksHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitNeuralNetworkPredictionExplainerPage(window, currentPage)
})
predictedOutcomeColumn.Add(neuralNetworksHelpButton)
} else {
rulesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitRulesPredictionExplainerPage(window, currentPage)
})
predictedOutcomeColumn.Add(rulesHelpButton)
}
neuralNetworkConfidenceHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
showUnderConstructionDialog(window)
//TODO
})
outcomeScoresColumn.Add(outcomeScoresHelpButton)
neuralNetworkPredictionConfidenceColumn.Add(neuralNetworkConfidenceHelpButton)
numberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitNumberOfRulesTestedExplainerPage(window, currentPage)
setDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
})
numberOfRulesTestedColumn.Add(numberOfRulesTestedHelpButton)
genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, outcomeScoresColumn, numberOfRulesTestedColumn, viewRulesButtonsColumn, layout.NewSpacer())
genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, predictedOutcomeColumn)
if (neuralNetworkExists == true){
genomesContainer.Add(neuralNetworkPredictionConfidenceColumn)
} else {
genomesContainer.Add(numberOfRulesTestedColumn)
}
genomesContainer.Add(viewDetailsButtonsColumn)
genomesContainer.Add(layout.NewSpacer())
return genomesContainer, nil
}
@ -2140,12 +2290,12 @@ func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIden
// Ths function provides a page to view the trait rules for a particular genome from a genetic analysis
func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte, genomeName string, previousPage func()){
// Ths function provides a page to view the discrete trait rules for a particular genome from a genetic analysis
func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte, genomeName string, previousPage func()){
setLoadingScreen(window, "Loading Trait Rules", "Loading trait rules...")
currentPage := func(){setViewPersonGenomeTraitRulesPage(window, geneticAnalysisObject, traitName, genomeIdentifier, genomeName, previousPage)}
currentPage := func(){setViewPersonGenomeDiscreteTraitRulesPage(window, geneticAnalysisObject, traitName, genomeIdentifier, genomeName, previousPage)}
title := getPageTitleCentered("View Trait Rules - " + traitName)
@ -2153,7 +2303,7 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject
description1 := widget.NewLabel("Below are the trait rule results for this genome.")
rulesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitRulesExplainerPage(window, currentPage)
setDiscreteTraitRulesExplainerPage(window, currentPage)
})
description1Row := container.NewHBox(layout.NewSpacer(), description1, rulesHelpButton, layout.NewSpacer())
@ -2195,7 +2345,7 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject
return
}
ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
@ -2221,7 +2371,7 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject
rulesTestedLabel := widget.NewLabel("Rules Tested:")
rulesTestedText := getBoldLabel(numberOfRulesTestedString + "/" + totalNumberOfRulesString)
rulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitNumberOfRulesTestedExplainerPage(window, currentPage)
setDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
})
rulesTestedRow := container.NewHBox(layout.NewSpacer(), rulesTestedLabel, rulesTestedText, rulesTestedHelpButton, layout.NewSpacer())
@ -2245,7 +2395,7 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil){ return err }
ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil) { return err }
getGenomePassesRuleText := func()string{
@ -2267,7 +2417,7 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject
// We do this because the rule effects column may be multiple rows tall
viewRuleButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewPersonGeneticAnalysisTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, currentPage)
setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, currentPage)
})
ruleIdentifierColumn.Add(ruleIdentifierLabel)
@ -2352,12 +2502,12 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject
}
ruleEffectsHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
})
ruleEffectsColumn.Add(ruleEffectsHelpButton)
genomePassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setGenomePassesTraitRuleExplainerPage(window, currentPage)
setGenomePassesDiscreteTraitRuleExplainerPage(window, currentPage)
})
genomePassesRuleColumn.Add(genomePassesRuleHelpButton)
@ -2381,9 +2531,9 @@ func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject
// This function provides a page to view the details of a specific trait rule from a person genetic analysis
// The page will show the rule details for all of the person's genomes
func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){
func setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){
currentPage := func(){setViewPersonGeneticAnalysisTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, previousPage)}
currentPage := func(){setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, previousPage)}
title := getPageTitleCentered("Trait Rule Details - " + traitName)
@ -2396,13 +2546,13 @@ func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneti
ruleIdentifierLabel := widget.NewLabel("Rule Identifier:")
ruleIdentifierText := getBoldLabel(ruleIdentifierHex)
ruleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
})
ruleIdentifierRow := container.NewHBox(layout.NewSpacer(), ruleIdentifierLabel, ruleIdentifierText, ruleInfoButton, layout.NewSpacer())
getGenomesRuleInfoGrid := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
if (err != nil) { return nil, err }
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -2413,7 +2563,7 @@ func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneti
addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{
genomeRuleStatusKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
genomeRuleStatusKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil) { return err }
getGenomePassesRuleText := func()string{
@ -2501,7 +2651,7 @@ func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneti
}
genomePassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setGenomePassesTraitRuleExplainerPage(window, currentPage)
setGenomePassesDiscreteTraitRuleExplainerPage(window, currentPage)
})
genomePassesRuleColumn.Add(genomePassesRuleHelpButton)

View file

@ -510,7 +510,7 @@ func setViewTraitDetailsPage(window fyne.Window, traitName string, previousPage
}
traitDescription := traitObject.TraitDescription
traitReferencesMap := traitObject.References
traitReferencesMap := traitObject.ReferencesMap
traitNameLabel := widget.NewLabel("Trait Name:")
traitNameText := getBoldLabel(traitName)
@ -538,9 +538,9 @@ func setViewTraitDetailsPage(window fyne.Window, traitName string, previousPage
setPageContent(page, window)
}
func setViewTraitRuleDetailsPage(window fyne.Window, traitName string, ruleIdentifier string, previousPage func()){
func setViewDiscreteTraitRuleDetailsPage(window fyne.Window, traitName string, ruleIdentifier string, previousPage func()){
currentPage := func(){setViewTraitRuleDetailsPage(window, traitName, ruleIdentifier, previousPage)}
currentPage := func(){setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifier, previousPage)}
title := getPageTitleCentered("Viewing Rule Details")
@ -561,7 +561,7 @@ func setViewTraitRuleDetailsPage(window fyne.Window, traitName string, ruleIdent
}
ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap
ruleReferencesMap := traitRuleObject.References
ruleReferencesMap := traitRuleObject.ReferencesMap
viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){
setViewGeneticAnalysisReferencesPage(window, "Rule", ruleReferencesMap, currentPage)
@ -605,7 +605,7 @@ func setViewTraitRuleDetailsPage(window fyne.Window, traitName string, ruleIdent
}
outcomeEffectHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
})
outcomeEffectColumn.Add(outcomeEffectHelpButton)

View file

@ -12,6 +12,7 @@ import "fyne.io/fyne/v2/widget"
import "seekia/resources/worldLanguages"
import "seekia/resources/worldLocations"
import "seekia/resources/geneticPredictionModels"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
import "seekia/resources/geneticReferences/traits"
@ -1270,7 +1271,7 @@ func setViewUserProfilePage_Category(window fyne.Window, profileIsMine bool, cat
})
geneticTraitsButton := widget.NewButton("Genetic Traits", func(){
setViewMateProfilePage_GeneticTraits(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
setViewMateProfilePage_GeneticTraits(window, getAnyUserProfileAttributeFunction, currentPage)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(2, racialSimilarityButton, totalDiseaseRiskButton, ancestryCompositionButton, monogenicDiseasesButton, haplogroupsButton, polygenicDiseasesButton, neanderthalVariantsButton, geneticTraitsButton))
@ -2097,9 +2098,9 @@ func setViewMateProfilePage_RacialSimilarity(window fyne.Window, getAnyUserProfi
if (err != nil) { return err }
traitLociList := traitObject.LociList
numberOfTraitLoci := len(traitLociList)
quantityOfTraitLoci := len(traitLociList)
numberOfTraitLociString := helpers.ConvertIntToString(numberOfTraitLoci)
quantityOfTraitLociString := helpers.ConvertIntToString(quantityOfTraitLoci)
geneticSimilarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(geneticSimilarityAttributeName)
if (err != nil) { return err }
@ -2108,11 +2109,11 @@ func setViewMateProfilePage_RacialSimilarity(window fyne.Window, getAnyUserProfi
geneticSimilarityColumn.Add(unknownLabel)
numberOfTestedLociText := "0/" + numberOfTraitLociString
quantityOfTestedLociText := "0/" + quantityOfTraitLociString
numberOfTestedLociLabel := getBoldLabelCentered(numberOfTestedLociText)
quantityOfTestedLociLabel := getBoldLabelCentered(quantityOfTestedLociText)
numberOfTestedLociColumn.Add(numberOfTestedLociLabel)
numberOfTestedLociColumn.Add(quantityOfTestedLociLabel)
} else {
similarityFormatted := attributeValue + "%"
@ -2147,7 +2148,7 @@ func setViewMateProfilePage_RacialSimilarity(window fyne.Window, getAnyUserProfi
numberOfTestedLociString := helpers.ConvertIntToString(numberOfTestedLoci)
numberOfTestedLociLabelText := numberOfTestedLociString + "/" + numberOfTraitLociString
numberOfTestedLociLabelText := numberOfTestedLociString + "/" + quantityOfTraitLociString
numberOfTestedLociLabel := getBoldLabelCentered(numberOfTestedLociLabelText)
@ -2959,24 +2960,25 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
//Outputs:
// -map[int64]locusValue.LocusValue
// -error
getMyDiseaseLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
anyMyLociValuesExist, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, myGenomeIdentifier)
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil) { return nil, err }
if (anyMyLociValuesExist == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
}
return myDiseaseLocusValuesMap, nil
return myGenomeLocusValuesMap, nil
}
myDiseaseLocusValuesMap, err := getMyDiseaseLocusValuesMap()
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil) { return nil, err }
// Map Structure: Locus rsID -> Locus Value
@ -3031,7 +3033,7 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
userDiseaseRiskScoreString, err := getUserDiseaseRiskScoreString()
if (err != nil) { return nil, err }
anyOffspringLociTested, offspringDiseaseRiskScore, offspringNumberOfLociTested, _, offspringSampleRiskScoresList, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLociList, myDiseaseLocusValuesMap, userDiseaseLocusValuesMap)
anyOffspringLociTested, offspringDiseaseRiskScore, offspringNumberOfLociTested, _, offspringSampleRiskScoresList, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLociList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) { return nil, err }
getOffspringDiseaseRiskScoreFormatted := func()(string, error){
@ -3172,7 +3174,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
// Outputs:
// -map[int64]locusValue.LocusValue: Map Structure: Locus rsID -> Locus Value
// -error
getMyDiseaseLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
@ -3183,17 +3185,18 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
return emptyMap, nil
}
anyLocusValuesExist, _, _, myLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, myGenomeIdentifier)
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil) { return nil, err }
if (anyLocusValuesExist == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
}
return myLocusValuesMap, nil
return myGenomeLocusValuesMap, nil
}
myDiseaseLocusValuesMap, err := getMyDiseaseLocusValuesMap()
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -3248,7 +3251,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
return
}
anyOffspringLociTested, _, offspringNumberOfLociTested, offspringLociInfoMap, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLocusObjectsList, myDiseaseLocusValuesMap, userDiseaseLocusValuesMap)
anyOffspringLociTested, _, offspringNumberOfLociTested, offspringLociInfoMap, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLocusObjectsList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -3513,9 +3516,9 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
setPageContent(page, window)
}
func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
func setViewMateProfilePage_GeneticTraits(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
currentPage := func(){setViewMateProfilePage_GeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
currentPage := func(){setViewMateProfilePage_GeneticTraits(window, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
@ -3523,7 +3526,46 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
subtitle := getPageSubtitleCentered(translate("Genetic Traits"))
description1 := getLabelCentered("Below is the genetic trait analysis for this user.")
description1 := getLabelCentered("Choose if you want to view Discrete traits or Numeric traits.")
description2 := getLabelCentered("Discrete traits are traits which have discrete outcomes, such as Eye Color")
description3 := getLabelCentered("Numeric traits are traits with numeric outcomes, such as Height.")
discreteTraitsButton := widget.NewButton("Discrete Traits", func(){
setViewMateProfilePage_DiscreteGeneticTraits(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
})
numericTraitsButton := widget.NewButton("Numeric Traits", func(){
//TODO
showUnderConstructionDialog(window)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, discreteTraitsButton, numericTraitsButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
if (userOrOffspring != "User" && userOrOffspring != "Offspring"){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiscreteGeneticTraits called with invalid userOrOffspring: " + userOrOffspring), previousPage)
return
}
if (userOrOffspring == "Offspring"){
setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...")
}
currentPage := func(){setViewMateProfilePage_DiscreteGeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Discrete Genetic Traits"))
description1 := getLabelCentered("Below is the discrete genetic trait analysis for this user.")
description2 := getLabelCentered("You can choose to view the analysis of the user or an offspring between you and the user.")
description3 := getLabelCentered("You must link your genome person in the Build Profile menu to see offspring information.")
@ -3531,7 +3573,7 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
if (userOrOffspring == newUserOrOffspring){
return
}
setViewMateProfilePage_GeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
setViewMateProfilePage_DiscreteGeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
}
userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton)
@ -3541,63 +3583,96 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
getTraitsInfoGrid := func()(*fyne.Container, error){
emptyLabelA := widget.NewLabel("")
traitNameLabel := getItalicLabelCentered("Trait Name")
emptyLabelB := widget.NewLabel("")
userOutcomeScoresLabel := getItalicLabelCentered("User Outcome Scores")
emptyLabelC := widget.NewLabel("")
offspringOutcomeScoresLabel := getItalicLabelCentered("Offspring Outcome Scores")
numberOfLabelA := getItalicLabelCentered("Number Of")
rulesTestedLabelA := getItalicLabelCentered("Rules Tested")
numberOfLabelB := getItalicLabelCentered("Number Of")
rulesTestedLabelB := getItalicLabelCentered("Rules Tested")
emptyLabelD := widget.NewLabel("")
emptyLabelE := widget.NewLabel("")
traitNameColumn := container.NewVBox(emptyLabelA, traitNameLabel, widget.NewSeparator())
userOutcomeScoresColumn := container.NewVBox(emptyLabelB, userOutcomeScoresLabel, widget.NewSeparator())
offspringOutcomeScoresColumn := container.NewVBox(emptyLabelC, offspringOutcomeScoresLabel, widget.NewSeparator())
userNumberOfRulesTestedColumn := container.NewVBox(numberOfLabelA, rulesTestedLabelA, widget.NewSeparator())
offspringNumberOfRulesTestedColumn := container.NewVBox(numberOfLabelB, rulesTestedLabelB, widget.NewSeparator())
viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator())
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
//Outputs:
// -map[int64]locusValue.LocusValue
// -error
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil) { return nil, err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
}
return myGenomeLocusValuesMap, nil
}
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil) { return nil, err }
emptyLabel1 := widget.NewLabel("")
traitNameLabel := getItalicLabelCentered("Trait Name")
emptyLabel2 := widget.NewLabel("")
userPredictedOutcomeTitle := getItalicLabelCentered("User Predicted Outcome")
emptyLabel3 := widget.NewLabel("")
offspringOutcomeProbabilitiesLabel := getItalicLabelCentered("Offspring Outcome Probabilities")
quantityOfLabel1 := getItalicLabelCentered("Quantity Of")
lociKnownLabel := getItalicLabelCentered("Loci Known")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator())
userPredictedOutcomeColumn := container.NewVBox(emptyLabel2, userPredictedOutcomeTitle, widget.NewSeparator())
offspringOutcomeProbabilitiesColumn := container.NewVBox(emptyLabel3, offspringOutcomeProbabilitiesLabel, widget.NewSeparator())
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel1, lociKnownLabel, widget.NewSeparator())
viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return nil, err }
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
continue
}
traitRulesList := traitObject.RulesList
totalNumberOfTraitRules := len(traitRulesList)
if (totalNumberOfTraitRules == 0){
traitLociList := traitObject.LociList
numberOfTraitLoci := len(traitLociList)
if (totalNumberOfTraitRules == 0 && numberOfTraitLoci == 0){
// We are not able to analyze these traits yet
// We will once we add neural network prediction
continue
}
totalNumberOfTraitRulesString := helpers.ConvertIntToString(totalNumberOfTraitRules)
traitOutcomeNamesList := traitObject.OutcomesList
// We have to sort outcome names so they always show up in the same order
traitOutcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(traitOutcomeNamesList)
traitNeuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
if (traitNeuralNetworkExists == false && totalNumberOfTraitRules == 0){
// We are not able to analyze these traits yet
continue
}
traitNameText := getBoldLabelCentered(translate(traitName))
traitNameColumn.Add(traitNameText)
viewTraitDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewMateProfilePage_TraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage)
if (traitNeuralNetworkExists == true){
//TODO
showUnderConstructionDialog(window)
} else {
setViewMateProfilePage_DiscreteTraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage)
}
})
viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton)
@ -3606,8 +3681,6 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
traitLociList := traitObject.LociList
for _, rsID := range traitLociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
@ -3635,200 +3708,244 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
userTraitLocusValuesMap[rsID] = userLocusValue
}
//Outputs:
// -bool: At least 1 rule is known
// -map[string]int: Outcome name -> Outcome score
// -int: Number of rules tested
// -error
getUserTraitOutcomeScoresMap := func()(bool, map[string]int, int, error){
userTraitOutcomeScoresMap := make(map[string]int)
userNumberOfRulesTested := 0
for _, traitRuleObject := range traitRulesList{
ruleLociList := traitRuleObject.LociList
userRuleStatusIsKnown, userPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return false, nil, 0, err }
if (userRuleStatusIsKnown == false){
continue
}
userNumberOfRulesTested += 1
if (userPassesRule == true){
ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap
for traitOutcome, pointsEffect := range ruleOutcomePointsMap{
userTraitOutcomeScoresMap[traitOutcome] += pointsEffect
}
}
}
if (userNumberOfRulesTested == 0){
return false, nil, 0, nil
}
traitOutcomesList := traitObject.OutcomesList
// We add all outcomes for which there were no points
for _, traitOutcome := range traitOutcomesList{
_, exists := userTraitOutcomeScoresMap[traitOutcome]
if (exists == false){
userTraitOutcomeScoresMap[traitOutcome] = 0
}
}
return true, userTraitOutcomeScoresMap, userNumberOfRulesTested, nil
}
//Outputs:
// -bool: At least 1 rule is known
// -map[string]float64: Outcome name -> Outcome score
// -int: Number of rules tested
// -error
getOffspringTraitOutcomeScoresMap := func()(bool, map[string]float64, int, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// Without my genome person chosen, all offspring rules and outcome scores are unknown
return false, nil, 0, nil
}
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, nil, 0, err }
anyRuleTested, offspringNumberOfRulesTested, _, offspringAverageOutcomeScoresMap, err := createCoupleGeneticAnalysis.GetOffspringTraitInfo(traitObject, myTraitLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, nil, 0, err }
if (anyRuleTested == false){
return false, nil, 0, nil
}
return true, offspringAverageOutcomeScoresMap, offspringNumberOfRulesTested, nil
}
if (userOrOffspring == "User"){
userTraitOutcomeScoresKnown, userTraitOutcomeScoresMap, userNumberOfRulesTested, err := getUserTraitOutcomeScoresMap()
if (err != nil) { return nil, err }
if (traitNeuralNetworkExists == true){
numberOfRulesTestedString := helpers.ConvertIntToString(userNumberOfRulesTested)
numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString
numberOfRulesTestedLabel := getBoldLabelCentered(numberOfRulesTestedFormatted)
userNumberOfRulesTestedColumn.Add(numberOfRulesTestedLabel)
traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, _, quantityOfLociKnown, _, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return nil, err }
if (traitNeuralNetworkExists == false){
return nil, errors.New("GetGenomeTraitAnalysis_NeuralNetwork claims neural network doesn't exist for trait, but we already checked.")
}
if (anyLocusValuesAreKnown == false){
unknownLabel := getItalicLabelCentered(translate("Unknown"))
userPredictedOutcomeColumn.Add(unknownLabel)
} else {
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
userPredictedOutcomeColumn.Add(predictedOutcomeLabel)
}
if (userTraitOutcomeScoresKnown == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
userOutcomeScoresColumn.Add(unknownLabel)
} else {
for index, outcomeName := range traitOutcomeNamesListSorted{
// We use the rules-based analysis
outcomeScore, exists := userTraitOutcomeScoresMap[outcomeName]
if (exists == false){
return nil, errors.New("Outcome not found in userTraitOutcomeScoresMap.")
}
anyRulesExist, _, quantityOfLociKnown_Rules, _, predictedOutcomeIsKnown, predictedOutcome, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_Rules(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return nil, err }
if (anyRulesExist == false){
return nil, errors.New("GetGenomeTraitAnalysis_Rules claims no rules exist when we already checked.")
}
outcomeScoreString := helpers.ConvertIntToString(outcomeScore)
if (predictedOutcomeIsKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
userPredictedOutcomeColumn.Add(unknownLabel)
} else {
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
userPredictedOutcomeColumn.Add(predictedOutcomeLabel)
}
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
userOutcomeScoresColumn.Add(outcomeRow)
traitLociList_Rules := traitObject.LociList_Rules
if (index > 0){
totalQuantityOfLoci := len(traitLociList_Rules)
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown_Rules)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
traitNameColumn.Add(emptyLabelA)
userNumberOfRulesTestedColumn.Add(emptyLabelB)
viewTraitDetailsButtonsColumn.Add(emptyLabelC)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
}
} else {
// userOrOffspring == "Offspring"
if (traitNeuralNetworkExists == true){
neuralNetworkExists, anyLociKnown, outcomeProbabilitiesMap, _, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false){
return nil, errors.New("GetOffspringTraitInfo_NeuralNetwork claiming that neural network doesn't exist when we already checked.")
}
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
if (anyLociKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
offspringOutcomeProbabilitiesColumn.Add(unknownLabel)
} else {
outcomesList := helpers.GetListOfMapKeys(outcomeProbabilitiesMap)
// We sort the outcomes in alphabetical order so they show up the same way each time
slices.Sort(outcomesList)
quantityOfAddedItems := 0
for _, outcomeName := range outcomesList{
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
return nil, errors.New("GetListOfMapKeys returning element which doesn't exist in map.")
}
if (outcomeProbability == 0){
continue
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
outcomeProbabilityLabelText := outcomeName + ": " + outcomeProbabilityString + "%"
outcomeProbabilityLabel := getBoldLabelCentered(outcomeProbabilityLabelText)
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilityLabel)
quantityOfAddedItems += 1
if (quantityOfAddedItems > 1){
// We add whitespace for the other columns
traitNameColumn.Add(widget.NewLabel(""))
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
viewTraitDetailsButtonsColumn.Add(widget.NewLabel(""))
}
}
}
}
} else if (userOrOffspring == "Offspring"){
offspringTraitOutcomeScoresKnown, offspringTraitOutcomeScoresMap, offspringNumberOfRulesTested, err := getOffspringTraitOutcomeScoresMap()
if (err != nil) { return nil, err }
numberOfRulesTestedString := helpers.ConvertIntToString(offspringNumberOfRulesTested)
numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString
numberOfRulesTestedLabel := getBoldLabelCentered(numberOfRulesTestedFormatted)
offspringNumberOfRulesTestedColumn.Add(numberOfRulesTestedLabel)
if (offspringTraitOutcomeScoresKnown == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
offspringOutcomeScoresColumn.Add(unknownLabel)
} else {
for index, outcomeName := range traitOutcomeNamesListSorted{
// We use the rules-based analysis
outcomeScore, exists := offspringTraitOutcomeScoresMap[outcomeName]
if (exists == false){
return nil, errors.New("Outcome not found in offspringTraitOutcomeScoresMap.")
}
anyRulesExist, rulesAnalysisExists, _, offspringQuantityOfLociKnown, _, outcomeProbabilitiesMap, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_Rules(traitObject, myGenomeLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return nil, err }
if (anyRulesExist == false){
return nil, errors.New("GetOffspringDiscreteTraitInfo_Rules claiming that no rules exist when we already checked.")
}
outcomeScoreString := helpers.ConvertFloat64ToStringRounded(outcomeScore, 2)
lociList_Rules := traitObject.LociList_Rules
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
offspringOutcomeScoresColumn.Add(outcomeRow)
totalQuantityOfLoci := len(lociList_Rules)
if (index > 0){
quantityOfLociKnownString := helpers.ConvertIntToString(offspringQuantityOfLociKnown)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
traitNameColumn.Add(emptyLabelA)
offspringNumberOfRulesTestedColumn.Add(emptyLabelB)
viewTraitDetailsButtonsColumn.Add(emptyLabelC)
if (rulesAnalysisExists == false){
unknownLabel := getItalicLabelCentered(translate("Unknown"))
offspringOutcomeProbabilitiesColumn.Add(unknownLabel)
} else {
outcomesList := helpers.GetListOfMapKeys(outcomeProbabilitiesMap)
// We sort the outcomes in alphabetical order so they show up the same way each time
slices.Sort(outcomesList)
quantityOfAddedItems := 0
for _, outcomeName := range outcomesList{
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
return nil, errors.New("GetListOfMapKeys returning element which doesn't exist in map.")
}
if (outcomeProbability == 0){
continue
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
outcomeProbabilityLabelText := outcomeName + ": " + outcomeProbabilityString + "%"
outcomeProbabilityLabel := getBoldLabelCentered(outcomeProbabilityLabelText)
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilityLabel)
quantityOfAddedItems += 1
if (quantityOfAddedItems > 1){
// We add whitespace for the other columns
traitNameColumn.Add(widget.NewLabel(""))
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
viewTraitDetailsButtonsColumn.Add(widget.NewLabel(""))
}
}
}
}
}
traitNameColumn.Add(widget.NewSeparator())
userOutcomeScoresColumn.Add(widget.NewSeparator())
offspringOutcomeScoresColumn.Add(widget.NewSeparator())
userNumberOfRulesTestedColumn.Add(widget.NewSeparator())
offspringNumberOfRulesTestedColumn.Add(widget.NewSeparator())
userPredictedOutcomeColumn.Add(widget.NewSeparator())
offspringOutcomeProbabilitiesColumn.Add(widget.NewSeparator())
quantityOfLociKnownColumn.Add(widget.NewSeparator())
viewTraitDetailsButtonsColumn.Add(widget.NewSeparator())
}
userOutcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitOutcomeScoresExplainerPage(window, currentPage)
if (userOrOffspring == "User"){
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
userPredictedOutcomeColumn.Add(predictedOutcomeHelpButton)
} else {
// userOrOffspring == "Offspring"
outcomeProbabilitiesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilitiesHelpButton)
}
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
offspringOutcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitOutcomeScoresExplainerPage(window, currentPage)
})
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
userNumberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitNumberOfRulesTestedExplainerPage(window, currentPage)
})
offspringNumberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitNumberOfRulesTestedExplainerPage(window, currentPage)
})
userOutcomeScoresColumn.Add(userOutcomeScoresHelpButton)
offspringOutcomeScoresColumn.Add(offspringOutcomeScoresHelpButton)
userNumberOfRulesTestedColumn.Add(userNumberOfRulesTestedHelpButton)
offspringNumberOfRulesTestedColumn.Add(offspringNumberOfRulesTestedHelpButton)
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn)
if (userOrOffspring == "User"){
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, userOutcomeScoresColumn, userNumberOfRulesTestedColumn, viewTraitDetailsButtonsColumn, layout.NewSpacer())
return traitsInfoGrid, nil
traitsInfoGrid.Add(userPredictedOutcomeColumn)
} else {
// userOrOffspring == "Offspring"
traitsInfoGrid.Add(offspringOutcomeProbabilitiesColumn)
}
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, offspringOutcomeScoresColumn, offspringNumberOfRulesTestedColumn, viewTraitDetailsButtonsColumn, layout.NewSpacer())
traitsInfoGrid.Add(quantityOfLociKnownColumn)
traitsInfoGrid.Add(viewTraitDetailsButtonsColumn)
traitsInfoGrid.Add(layout.NewSpacer())
return traitsInfoGrid, nil
}
@ -3844,9 +3961,9 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
setPageContent(page, window)
}
func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
currentPage := func(){setViewMateProfilePage_TraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
currentPage := func(){setViewMateProfilePage_DiscreteTraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered("View Profile - Physical")
@ -3868,7 +3985,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
// -bool: Any trait locus value exists for this myself
// -map[int64]locusValue.LocusValue: My locus values map
// -error
getMyTraitLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
getMyGenomeLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return false, nil, err }
@ -3878,17 +3995,18 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
return false, nil, nil
}
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, nil, err }
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil){ return false, nil, err }
if (len(myTraitLocusValuesMap) == 0){
return false, nil, nil
myGenomeMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which does not contain genome matching myGenomeIdentifier")
}
return true, myTraitLocusValuesMap, nil
return true, myGenomeMap, nil
}
anyMyTraitLocusValuesExist, myTraitLocusValuesMap, err := getMyTraitLocusValuesMap()
anyMyLocusValuesExist, myLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
@ -3900,9 +4018,14 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
return
}
traitLociList := traitObject.LociList
traitLociList_Rules := traitObject.LociList_Rules
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiscreteTraitRules called with trait which has no rules."), previousPage)
return
}
//Outputs:
// -bool: Any trait locus value exists for this user
// -map[int64]locusValue.LocusValue: User locus values map
@ -3913,7 +4036,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, rsID := range traitLociList{
for _, rsID := range traitLociList_Rules{
rsIDString := helpers.ConvertInt64ToString(rsID)
@ -3959,12 +4082,15 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
// -error
getOffspringProbabilityOfPassingRulesMap := func()(bool, map[[3]byte]int, error){
if (anyMyTraitLocusValuesExist == false || anyUserTraitLocusValueExists == false){
if (anyMyLocusValuesExist == false || anyUserTraitLocusValueExists == false){
return false, nil, nil
}
anyOffspringRulesTested, _, offspringProbabilityOfPassingRulesMap, _, err := createCoupleGeneticAnalysis.GetOffspringTraitInfo(traitObject, myTraitLocusValuesMap, userTraitLocusValuesMap)
anyRulesExist, anyOffspringRulesTested, _, _, offspringProbabilityOfPassingRulesMap, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_Rules(traitObject, myLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, nil, err }
if (anyRulesExist == false){
return false, nil, errors.New("GetOffspringDiscreteTraitInfo claiming no trait rules exist when we already checked.")
}
if (anyOffspringRulesTested == false){
return false, nil, nil
}
@ -4000,7 +4126,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
ruleLociList := ruleObject.LociList
ruleStatusIsKnown, _, err := createPersonGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
ruleStatusIsKnown, _, err := createPersonGeneticAnalysis.GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return 0, err }
if (ruleStatusIsKnown == true){
numberOfRulesTested += 1
@ -4027,9 +4153,9 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
numberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
if (userOrOffspring == "User"){
setTraitNumberOfRulesTestedExplainerPage(window, currentPage)
setDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
} else {
setOffspringTraitNumberOfRulesTestedExplainerPage(window, currentPage)
setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
}
})
@ -4093,7 +4219,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
getUserPassesRuleString := func()(string, error){
userRuleStatusIsKnown, userPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
userRuleStatusIsKnown, userPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return "", err }
if (userRuleStatusIsKnown == false){
@ -4134,7 +4260,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
// We do this because the rule effects column may be multiple rows tall
viewRuleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
})
ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifierHex)
userPassesRuleLabel := getBoldLabelCentered(userPassesRuleString)
@ -4200,11 +4326,11 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
}
ruleEffectsHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
})
userPassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setPersonPassesTraitRuleExplainerPage(window, currentPage)
setPersonPassesDiscreteTraitRuleExplainerPage(window, currentPage)
})
offspringProbabilityOfPassingRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
@ -4491,29 +4617,29 @@ func get23andMeAncestryCompositionDisplay(inputContinentPercentagesMap map[strin
// We sort region subregions list
// We sort them from highest to lowest in percentage.
compareSubregionsFunction := func(subregionADescription string, subregionBDescription string)int{
compareSubregionsFunction := func(subregion1Description string, subregion2Description string)int{
if (subregionADescription == subregionBDescription){
if (subregion1Description == subregion2Description){
panic("compareSubregionsFunction called with identical subregion descriptions.")
}
subregionAPercentage, exists := subregionDescriptionPercentagesMap[subregionADescription]
subregion1Percentage, exists := subregionDescriptionPercentagesMap[subregion1Description]
if (exists == false){
panic("subregionPercentagesMap missing subregion during sort.")
}
subregionBPercentage, exists := subregionDescriptionPercentagesMap[subregionBDescription]
subregion2Percentage, exists := subregionDescriptionPercentagesMap[subregion2Description]
if (exists == false){
panic("subregionPercentagesMap missing subregion during sort.")
}
if (subregionAPercentage == subregionBPercentage){
if (subregion1Percentage == subregion2Percentage){
// We sort subregions in unicode order
if (subregionADescription < subregionBDescription){
if (subregion1Description < subregion2Description){
return -1
}
return 1
}
if (subregionAPercentage > subregionBPercentage){
if (subregion1Percentage > subregion2Percentage){
return -1
}
@ -4527,32 +4653,33 @@ func get23andMeAncestryCompositionDisplay(inputContinentPercentagesMap map[strin
// We sort continent regions list by highest to lowest percentage.
compareRegionsFunction := func(regionADescription string, regionBDescription string)int{
compareRegionsFunction := func(region1Description string, region2Description string)int{
if (regionADescription == regionBDescription){
if (region1Description == region2Description){
panic("compareRegionsFunction called with identical regions.")
}
regionAPercentage, exists := regionDescriptionPercentagesMap[regionADescription]
region1Percentage, exists := regionDescriptionPercentagesMap[region1Description]
if (exists == false){
panic("regionPercentagesMap missing subregion during sort.")
}
regionBPercentage, exists := regionDescriptionPercentagesMap[regionBDescription]
region2Percentage, exists := regionDescriptionPercentagesMap[region2Description]
if (exists == false){
panic("regionPercentagesMap missing subregion during sort.")
}
if (regionAPercentage == regionBPercentage){
if (region1Percentage == region2Percentage){
// We sort regions in unicode order
if (regionADescription < regionBDescription){
if (region1Description < region2Description){
return -1
}
return 1
}
if (regionAPercentage > regionBPercentage){
if (region1Percentage > region2Percentage){
return -1
}
return 1
}
@ -4563,29 +4690,29 @@ func get23andMeAncestryCompositionDisplay(inputContinentPercentagesMap map[strin
// We sort root list by highest to lowest proportions
compareContinentsFunction := func(continentADescription string, continentBDescription string)int{
compareContinentsFunction := func(continent1Description string, continent2Description string)int{
if (continentADescription == continentBDescription){
if (continent1Description == continent2Description){
panic("compareContinentsFunction called with identical continents.")
}
continentAPercentage, exists := continentDescriptionPercentagesMap[continentADescription]
continent1Percentage, exists := continentDescriptionPercentagesMap[continent1Description]
if (exists == false){
panic("Continent percentage not found when sorting root list.")
}
continentBPercentage, exists := continentDescriptionPercentagesMap[continentBDescription]
continent2Percentage, exists := continentDescriptionPercentagesMap[continent2Description]
if (exists == false){
panic("Continent percentage not found when sorting root list.")
}
if (continentAPercentage == continentBPercentage){
if (continent1Percentage == continent2Percentage){
// We sort continents in unicode order
if (continentADescription < continentBDescription){
if (continent1Description < continent2Description){
return -1
}
return 1
}
if (continentAPercentage > continentBPercentage){
if (continent1Percentage > continent2Percentage){
return -1
}
return 1

View file

@ -932,7 +932,7 @@ func GetFakeProfile(profileType string, identityPublicKey [32]byte, identityPriv
traitLociList := traitObject.LociList
for _, rsID := range traitLociList{
for _, rsID := range traitLociList{
shareableRSIDsMap[rsID] = struct{}{}
}

View file

@ -9,21 +9,10 @@ package createCoupleGeneticAnalysis
// Disclaimer: I am a novice in the ways of genetics. This package could be flawed in numerous ways.
// TODO: We want to eventually use neural nets for both trait and polygenic disease analysis (see geneticPrediction.go)
// These will be trained on a set of genomes and will output a probability analysis for each trait/disease
// TODO: We want to eventually use neural nets for polygenic disease analysis (see geneticPrediction.go)
// This is only possible once we get access to the necessary training data
//
// This is how offspring trait prediction could work with the neural net model:
// Both users will share all relevant SNPS base pairs that determine the trait on their profile.
// Each location has 4 possible outcomes, so for 1000 SNPs, there are 4^1000 possible offspring outcomes for a given couple. (this
// is actually too high because recombination break points do not occur at each locus, see genetic linkage)
// This is too many options for us to check all of them.
// Seekia will create 100 offspring that would be produced from both users, and run each offspring through the neural net.
// Each offspring would be different. The allele from each parent for each SNP would be randomly chosen.
// The user can choose how many prospective offspring to create in the settings.
// More offspring will take longer, but will yield a more accurate trait probability.
// Seekia will show the the average trait result and a chart showing the trait results for all created offspring.
import "seekia/resources/geneticPredictionModels"
import "seekia/resources/geneticReferences/locusMetadata"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
@ -40,6 +29,7 @@ import "errors"
import mathRand "math/rand/v2"
import "slices"
import "maps"
import "reflect"
//Outputs:
@ -86,6 +76,30 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
return false, "", nil
}
// This map stores each genome's locus values
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
person1GenomesMap := make(map[[16]byte]map[int64]locusValue.LocusValue)
for _, genomeWithMetadata := range person1GenomesWithMetadataList{
genomeIdentifier := genomeWithMetadata.GenomeIdentifier
genomeMap := genomeWithMetadata.GenomeMap
person1GenomesMap[genomeIdentifier] = genomeMap
}
// This map stores each genome's locus values
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
person2GenomesMap := make(map[[16]byte]map[int64]locusValue.LocusValue)
for _, genomeWithMetadata := range person2GenomesWithMetadataList{
genomeIdentifier := genomeWithMetadata.GenomeIdentifier
genomeMap := genomeWithMetadata.GenomeMap
person2GenomesMap[genomeIdentifier] = genomeMap
}
// The analysis will analyze either 1 or 2 genome pairs
// The gui will display the results from each pair
//Outputs:
@ -172,9 +186,9 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
person2DiseaseAnalysisObject, err := createPersonGeneticAnalysis.GetPersonMonogenicDiseaseAnalysis(person2GenomesWithMetadataList, diseaseObject)
if (err != nil) { return false, "", err }
// This map stores the number of variants tested in each person's genome
// This map stores the quantity of variants tested in each person's genome
// Map Structure: Genome Identifier -> Number of variants tested
numberOfVariantsTestedMap := make(map[[16]byte]int)
quantityOfVariantsTestedMap := make(map[[16]byte]int)
// This map stores the offspring disease probabilities for each genome pair.
// A genome pair is a concatenation of two genome identifiers
@ -184,7 +198,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
// This will calculate the probability of monogenic disease for the offspring from the two specified genomes
// It also calculates the probabilities for each monogenic disease variant for the offspring
// It then adds the genome pair disease information to the offspringMonogenicDiseaseInfoMap and numberOfVariantsTestedMap
// It then adds the genome pair disease information to the offspringMonogenicDiseaseInfoMap and quantityOfVariantsTestedMap
addGenomePairInfoToDiseaseMaps := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
//Outputs:
@ -202,9 +216,9 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
personGenomeProbabilityOfPassingADiseaseVariant := personGenomeDiseaseInfoObject.ProbabilityOfPassingADiseaseVariant
personGenomeNumberOfVariantsTested := personGenomeDiseaseInfoObject.NumberOfVariantsTested
personGenomeQuantityOfVariantsTested := personGenomeDiseaseInfoObject.QuantityOfVariantsTested
return true, personGenomeProbabilityOfPassingADiseaseVariant, personGenomeNumberOfVariantsTested
return true, personGenomeProbabilityOfPassingADiseaseVariant, personGenomeQuantityOfVariantsTested
}
person1ProbabilityIsKnown, person1WillPassVariantProbability, person1NumberOfVariantsTested := getPersonWillPassDiseaseVariantProbability(person1DiseaseAnalysisObject, person1GenomeIdentifier)
@ -362,8 +376,8 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
return nil
}
numberOfVariantsTestedMap[person1GenomeIdentifier] = person1NumberOfVariantsTested
numberOfVariantsTestedMap[person2GenomeIdentifier] = person2NumberOfVariantsTested
quantityOfVariantsTestedMap[person1GenomeIdentifier] = person1NumberOfVariantsTested
quantityOfVariantsTestedMap[person2GenomeIdentifier] = person2NumberOfVariantsTested
newOffspringGenomePairMonogenicDiseaseInfoObject := geneticAnalysis.OffspringGenomePairMonogenicDiseaseInfo{
ProbabilityOffspringHasDiseaseIsKnown: offspringHasDiseaseProbabilityIsKnown,
@ -390,7 +404,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
}
newOffspringMonogenicDiseaseInfoObject := geneticAnalysis.OffspringMonogenicDiseaseInfo{
NumberOfVariantsTestedMap: numberOfVariantsTestedMap,
QuantityOfVariantsTestedMap: quantityOfVariantsTestedMap,
MonogenicDiseaseInfoMap: offspringMonogenicDiseaseInfoMap,
}
@ -480,12 +494,6 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
diseaseName := diseaseObject.DiseaseName
diseaseLociList := diseaseObject.LociList
person1DiseaseAnalysisObject, err := createPersonGeneticAnalysis.GetPersonPolygenicDiseaseAnalysis(person1GenomesWithMetadataList, diseaseObject)
if (err != nil) { return false, "", err }
person2DiseaseAnalysisObject, err := createPersonGeneticAnalysis.GetPersonPolygenicDiseaseAnalysis(person2GenomesWithMetadataList, diseaseObject)
if (err != nil) { return false, "", err }
// This map stores the polygenic disease info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairPolygenicDiseaseInfo
offspringPolygenicDiseaseInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo)
@ -494,40 +502,17 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
// It then adds the pair entry to the offspringPolygenicDiseaseInfoMap
addGenomePairDiseaseInfoToDiseaseMap := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
//Outputs:
// -bool: Any locus values exist
// -map[int64]locusValue.LocusValue
// -error
getPersonGenomeDiseaseLocusValuesMap := func(personGenomeIdentifier [16]byte, personDiseaseAnalysisObject geneticAnalysis.PersonPolygenicDiseaseInfo)(bool, map[int64]locusValue.LocusValue, error){
personPolygenicDiseaseInfoMap := personDiseaseAnalysisObject.PolygenicDiseaseInfoMap
personGenomeDiseaseInfoObject, exists := personPolygenicDiseaseInfoMap[personGenomeIdentifier]
if (exists == false){
// This person's genome has no information about loci related to this disease
return false, nil, nil
}
personGenomeLocusValuesMap := personGenomeDiseaseInfoObject.LocusValuesMap
return true, personGenomeLocusValuesMap, nil
person1LocusValuesMap, exists := person1GenomesMap[person1GenomeIdentifier]
if (exists == false){
return errors.New("addGenomePairDiseaseInfoToDiseaseMap called with unknown person1GenomeIdentifier.")
}
anyPerson1LociValuesExist, person1LocusValuesMap, err := getPersonGenomeDiseaseLocusValuesMap(person1GenomeIdentifier, person1DiseaseAnalysisObject)
if (err != nil) { return err }
if (anyPerson1LociValuesExist == false){
// Offspring's disease info for this locus on this genome pair is unknown
return nil
person2LocusValuesMap, exists := person2GenomesMap[person2GenomeIdentifier]
if (exists == false){
return errors.New("addGenomePairDiseaseInfoToDiseaseMap called with unknown person2GenomeIdentifier.")
}
anyPerson2LociValuesExist, person2LocusValuesMap, err := getPersonGenomeDiseaseLocusValuesMap(person2GenomeIdentifier, person2DiseaseAnalysisObject)
if (err != nil) { return err }
if (anyPerson2LociValuesExist == false){
// Offspring's disease info for this locus on this genome pair is unknown
return nil
}
anyOffspringLocusTested, genomePairOffspringAverageRiskScore, numberOfLociTested, genomePairOffspringDiseaseLociInfoMap, genomePairSampleOffspringRiskScoresList, err := GetOffspringPolygenicDiseaseInfo(diseaseLociList, person1LocusValuesMap, person2LocusValuesMap)
anyOffspringLocusTested, genomePairOffspringAverageRiskScore, quantityOfLociTested, genomePairOffspringDiseaseLociInfoMap, genomePairSampleOffspringRiskScoresList, err := GetOffspringPolygenicDiseaseInfo(diseaseLociList, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return err }
if (anyOffspringLocusTested == false){
// We have no information about this genome pair's disease risk
@ -537,7 +522,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
newOffspringGenomePairPolygenicDiseaseInfo := geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo{
NumberOfLociTested: numberOfLociTested,
QuantityOfLociTested: quantityOfLociTested,
OffspringAverageRiskScore: genomePairOffspringAverageRiskScore,
LociInfoMap: genomePairOffspringDiseaseLociInfoMap,
SampleOffspringRiskScoresList: genomePairSampleOffspringRiskScoresList,
@ -570,34 +555,20 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
checkIfConflictExists := func()(bool, error){
numberOfLociTested := 0
offspringAverageRiskScore := 0
offspringLociInfoMap := make(map[[3]byte]geneticAnalysis.OffspringPolygenicDiseaseLocusInfo)
currentGenomePairPolygenicDiseaseInfo := geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo{}
firstItemReached := false
for _, genomePairDiseaseInfoObject := range offspringPolygenicDiseaseInfoMap{
genomePairNumberOfLociTested := genomePairDiseaseInfoObject.NumberOfLociTested
genomePairOffspringAverageRiskScore := genomePairDiseaseInfoObject.OffspringAverageRiskScore
genomePairLociInfoMap := genomePairDiseaseInfoObject.LociInfoMap
if (firstItemReached == false){
numberOfLociTested = genomePairNumberOfLociTested
offspringAverageRiskScore = genomePairOffspringAverageRiskScore
offspringLociInfoMap = genomePairLociInfoMap
currentGenomePairPolygenicDiseaseInfo = genomePairDiseaseInfoObject
firstItemReached = true
continue
}
if (numberOfLociTested != genomePairNumberOfLociTested){
return true, nil
}
if (offspringAverageRiskScore != genomePairOffspringAverageRiskScore){
return true, nil
}
areEqual := maps.Equal(offspringLociInfoMap, genomePairLociInfoMap)
areEqual := reflect.DeepEqual(genomePairDiseaseInfoObject, currentGenomePairPolygenicDiseaseInfo)
if (areEqual == false){
// A conflict exists
return true, nil
}
}
@ -622,128 +593,140 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
if (err != nil) { return false, "", err }
// Map Structure: Trait Name -> Trait Info Object
offspringTraitsMap := make(map[string]geneticAnalysis.OffspringTraitInfo)
offspringDiscreteTraitsMap := make(map[string]geneticAnalysis.OffspringDiscreteTraitInfo)
// Map Structure: Trait Name -> Trait Info Object
offspringNumericTraitsMap := make(map[string]geneticAnalysis.OffspringNumericTraitInfo)
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
person1TraitAnalysisObject, err := createPersonGeneticAnalysis.GetPersonTraitAnalysis(person1GenomesWithMetadataList, traitObject)
if (err != nil) { return false, "", err }
if (traitIsDiscreteOrNumeric == "Discrete"){
person2TraitAnalysisObject, err := createPersonGeneticAnalysis.GetPersonTraitAnalysis(person2GenomesWithMetadataList, traitObject)
if (err != nil) { return false, "", err }
// This map stores the trait info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairDiscreteTraitInfo
offspringTraitInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairDiscreteTraitInfo)
// This map stores the trait info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairTraitInfo
offspringTraitInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairTraitInfo)
// This will add the offspring trait information for the provided genome pair to the offspringTraitInfoMap
addGenomePairTraitInfoToOffspringMap := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
// This will add the offspring trait information for the provided genome pair to the offspringTraitInfoMap
addGenomePairTraitInfoToOffspringMap := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
person1LocusValuesMap, exists := person1GenomesMap[person1GenomeIdentifier]
if (exists == false){
return errors.New("addGenomePairTraitInfoToOffspringMap called with unknown person1GenomeIdentifier.")
}
person1TraitInfoMap := person1TraitAnalysisObject.TraitInfoMap
person2TraitInfoMap := person2TraitAnalysisObject.TraitInfoMap
person2LocusValuesMap, exists := person2GenomesMap[person2GenomeIdentifier]
if (exists == false){
return errors.New("addGenomePairTraitInfoToOffspringMap called with unknown person2GenomeIdentifier.")
}
person1GenomeTraitInfoObject, exists := person1TraitInfoMap[person1GenomeIdentifier]
if (exists == false){
// This person has no genome values for any loci for this trait
// No predictions are possible
return nil
}
person2GenomeTraitInfoObject, exists := person2TraitInfoMap[person2GenomeIdentifier]
if (exists == false){
// This person has no genome values for any loci for this trait
// No predictions are possible
return nil
}
newOffspringGenomePairTraitInfo := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo{}
person1LocusValuesMap := person1GenomeTraitInfoObject.LocusValuesMap
person2LocusValuesMap := person2GenomeTraitInfoObject.LocusValuesMap
neuralNetworkExists, neuralNetworkAnalysisExists, outcomeProbabilitiesMap, averagePredictionConfidence, quantityOfLociTested, quantityOfParentalPhasedLoci, err := GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return err }
if (neuralNetworkExists == true){
anyRulesTested, numberOfRulesTested, offspringProbabilityOfPassingRulesMap, offspringAverageOutcomeScoresMap, err := GetOffspringTraitInfo(traitObject, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return err }
if (anyRulesTested == false){
// No rules were tested for this trait
// We will not add anything to the trait info map for this genome pair
return nil
}
newOffspringGenomePairTraitInfo.NeuralNetworkExists = true
newOffspringGenomePairTraitInfoObject := geneticAnalysis.OffspringGenomePairTraitInfo{
NumberOfRulesTested: numberOfRulesTested,
OffspringAverageOutcomeScoresMap: offspringAverageOutcomeScoresMap,
ProbabilityOfPassingRulesMap: offspringProbabilityOfPassingRulesMap,
}
if (neuralNetworkAnalysisExists == true){
newOffspringGenomePairTraitInfo.NeuralNetworkAnalysisExists = true
genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier)
newOffspringGenomePairTraitInfo_NeuralNetwork := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo_NeuralNetwork{
offspringTraitInfoMap[genomePairIdentifier] = newOffspringGenomePairTraitInfoObject
OffspringOutcomeProbabilitiesMap: outcomeProbabilitiesMap,
AverageConfidence: averagePredictionConfidence,
QuantityOfLociKnown: quantityOfLociTested,
QuantityOfParentalPhasedLoci: quantityOfParentalPhasedLoci,
}
return nil
}
err = addGenomePairTraitInfoToOffspringMap(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier)
if (err != nil) { return false, "", err }
if (genomePair2Exists == true){
err := addGenomePairTraitInfoToOffspringMap(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier)
if (err != nil) { return false, "", err }
}
newOffspringTraitInfoObject := geneticAnalysis.OffspringTraitInfo{
TraitInfoMap: offspringTraitInfoMap,
}
if (len(offspringTraitInfoMap) >= 2){
// We check for conflicts
// Conflicts are only possible if two genome pairs exist with information about the trait
checkIfConflictExists := func()(bool, error){
// We check for conflicts between each genome pair's outcome scores and trait rules maps
offspringAverageOutcomeScoresMap := make(map[string]float64)
offspringProbabilityOfPassingRulesMap := make(map[[3]byte]int)
firstItemReached := false
for _, genomePairTraitInfoObject := range offspringTraitInfoMap{
currentOffspringAverageOutcomeScoresMap := genomePairTraitInfoObject.OffspringAverageOutcomeScoresMap
currentProbabilityOfPassingRulesMap := genomePairTraitInfoObject.ProbabilityOfPassingRulesMap
if (firstItemReached == false){
offspringAverageOutcomeScoresMap = currentOffspringAverageOutcomeScoresMap
offspringProbabilityOfPassingRulesMap = currentProbabilityOfPassingRulesMap
firstItemReached = true
continue
}
areEqual := maps.Equal(offspringAverageOutcomeScoresMap, currentOffspringAverageOutcomeScoresMap)
if (areEqual == false){
return true, nil
}
areEqual = maps.Equal(offspringProbabilityOfPassingRulesMap, currentProbabilityOfPassingRulesMap)
if (areEqual == false){
return true, nil
newOffspringGenomePairTraitInfo.NeuralNetworkAnalysis = newOffspringGenomePairTraitInfo_NeuralNetwork
}
}
return false, nil
anyRulesExist, rulesAnalysisExists, quantityOfRulesTested, quantityOfLociKnown, offspringProbabilityOfPassingRulesMap, offspringOutcomeProbabilitiesMap, err := GetOffspringDiscreteTraitInfo_Rules(traitObject, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return err }
if (anyRulesExist == true){
newOffspringGenomePairTraitInfo.RulesExist = true
if (rulesAnalysisExists == true){
newOffspringGenomePairTraitInfo.RulesAnalysisExists = true
newOffspringGenomePairTraitInfo_Rules := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo_Rules{
QuantityOfRulesTested: quantityOfRulesTested,
QuantityOfLociKnown: quantityOfLociKnown,
ProbabilityOfPassingRulesMap: offspringProbabilityOfPassingRulesMap,
OffspringOutcomeProbabilitiesMap: offspringOutcomeProbabilitiesMap,
}
newOffspringGenomePairTraitInfo.RulesAnalysis = newOffspringGenomePairTraitInfo_Rules
}
}
genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier)
offspringTraitInfoMap[genomePairIdentifier] = newOffspringGenomePairTraitInfo
return nil
}
conflictExists, err := checkIfConflictExists()
err = addGenomePairTraitInfoToOffspringMap(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier)
if (err != nil) { return false, "", err }
newOffspringTraitInfoObject.ConflictExists = conflictExists
}
if (genomePair2Exists == true){
offspringTraitsMap[traitName] = newOffspringTraitInfoObject
err := addGenomePairTraitInfoToOffspringMap(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier)
if (err != nil) { return false, "", err }
}
newOffspringTraitInfoObject := geneticAnalysis.OffspringDiscreteTraitInfo{
TraitInfoMap: offspringTraitInfoMap,
}
if (len(offspringTraitInfoMap) >= 2){
// We check for conflicts
// Conflicts are only possible if two genome pairs exist with information about the trait
checkIfConflictExists := func()(bool, error){
// We check for conflicts between each genome pair's outcome scores and trait rules maps
genomePairTraitInfoObject := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo{}
firstItemReached := false
for _, currentGenomePairTraitInfoObject := range offspringTraitInfoMap{
if (firstItemReached == false){
genomePairTraitInfoObject = currentGenomePairTraitInfoObject
firstItemReached = true
continue
}
areEqual := reflect.DeepEqual(genomePairTraitInfoObject, currentGenomePairTraitInfoObject)
if (areEqual == false){
return true, nil
}
}
return false, nil
}
conflictExists, err := checkIfConflictExists()
if (err != nil) { return false, "", err }
newOffspringTraitInfoObject.ConflictExists = conflictExists
}
offspringDiscreteTraitsMap[traitName] = newOffspringTraitInfoObject
}
}
newCoupleAnalysis.TraitsMap = offspringTraitsMap
newCoupleAnalysis.DiscreteTraitsMap = offspringDiscreteTraitsMap
newCoupleAnalysis.NumericTraitsMap = offspringNumericTraitsMap
analysisBytes, err := encoding.EncodeMessagePackBytes(newCoupleAnalysis)
if (err != nil) { return false, "", err }
@ -1136,144 +1119,187 @@ func GetOffspringPolygenicDiseaseInfo(diseaseLociList []polygenicDiseases.Diseas
//Outputs:
// -bool: Any rules tested (if false, no offspring trait information is known)
// -int: Number of rules tested
// -bool: A neural network exists for this trait
// -bool: Analysis exists (at least 1 locus exists for this analysis from both people's genomes
// -map[string]int: Outcome probabilities map
// Map Structure: Outcome Name -> Offspring probability of outcome
// -int: Average prediction confidence (the average prediction confidence for all prospective offspring)
// -int: Quantity of loci tested
// -int: Quantity of parental phased loci
// -error
func GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, map[string]int, int, int, int, error){
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
return false, false, nil, 0, 0, 0, errors.New("GetOffspringDiscreteTraitInfo_NeuralNetwork called with non-discrete trait.")
}
modelExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
if (modelExists == false){
// Neural network prediction is not possible for this trait
return false, false, nil, 0, 0, 0, nil
}
traitLociList := traitObject.LociList
// First we count up the quantity of parental phased loci
// We only count the quantity of phased loci for loci which are known for both parents
quantityOfParentalPhasedLoci := 0
for _, rsID := range traitLociList{
person1LocusValue, exists := person1LocusValuesMap[rsID]
if (exists == false){
continue
}
person2LocusValue, exists := person2LocusValuesMap[rsID]
if (exists == false){
continue
}
person1LocusIsPhased := person1LocusValue.LocusIsPhased
if (person1LocusIsPhased == true){
quantityOfParentalPhasedLoci += 1
}
person2LocusIsPhased := person2LocusValue.LocusIsPhased
if (person2LocusIsPhased == true){
quantityOfParentalPhasedLoci += 1
}
}
// Next, we create 100 prospective offspring genomes.
anyLocusValueExists, prospectiveOffspringGenomesList, err := getProspectiveOffspringGenomesList(traitLociList, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return false, false, nil, 0, 0, 0, err }
if (anyLocusValueExists == false){
return true, false, nil, 0, 0, 0, nil
}
// Map Structure: Outcome Name -> Probability of outcome coming true
// Because we are summing from 100 offspring, the count of outcomes is the same as the probability of an offspring having the outcome
outcomeCountsMap := make(map[string]int)
// This is a sum of each prediction's confidence
predictionConfidencesSum := 0
quantityOfLociTested := 0
for index, offspringGenomeMap := range prospectiveOffspringGenomesList{
neuralNetworkExists, predictionIsKnown, predictedOutcome, predictionConfidence, currentQuantityOfLociTested, _, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, offspringGenomeMap, false)
if (err != nil){ return false, false, nil, 0, 0, 0, err }
if (neuralNetworkExists == false){
return false, false, nil, 0, 0, 0, errors.New("GetGenomeTraitAnalysis_NeuralNetwork claiming that neural network doesn't exist when we already checked.")
}
if (predictionIsKnown == false){
return false, false, nil, 0, 0, 0, errors.New("GetGenomeTraitAnalysis_NeuralNetwork claiming that prediction is impossible when we already know at least 1 locus value exists for trait.")
}
outcomeCountsMap[predictedOutcome] += 1
predictionConfidencesSum += predictionConfidence
if (index == 0){
// This value should be the same for each predicted offspring
quantityOfLociTested = currentQuantityOfLociTested
}
}
averagePredictionConfidence := predictionConfidencesSum/100
return true, true, outcomeCountsMap, averagePredictionConfidence, quantityOfLociTested, quantityOfParentalPhasedLoci, nil
}
//Outputs:
// -bool: Any rules exist (if false, rule-based prediction is not possible for this trait)
// -bool: Rule-based analysis exists (if false, no offspring trait information is known, or there is an outcome tie for one of the offspring)
// -int: Quantity of rules tested
// -int: Quantity of loci known
// -map[[3]byte]int: Offspring probability of passing rules map
// Map Structure: Rule identifier -> Offspring probability of passing rule (1-100)
// -map[string]float64: Offspring average outcome scores map
// Map Structure: Outcome Name -> Offspring average outcome score
// If a rule entry doesn't exist, we don't know the passes-rule probability for any of the offspring
// -map[string]int: Offspring outcome probabilities map
// Map Structure: Outcome Name -> Offspring probability of outcome (0-100)
// -error
func GetOffspringTraitInfo(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, int, map[[3]byte]int, map[string]float64, error){
func GetOffspringDiscreteTraitInfo_Rules(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, int, int, map[[3]byte]int, map[string]int, error){
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
return false, false, 0, 0, nil, nil, nil
}
if (len(person1LocusValuesMap) == 0){
return false, 0, nil, nil, nil
return true, false, 0, 0, nil, nil, nil
}
if (len(person2LocusValuesMap) == 0){
return false, 0, nil, nil, nil
return true, false, 0, 0, nil, nil, nil
}
// First, we create 100 prospective offspring genomes.
traitLociList := traitObject.LociList
traitLociList_Rules := traitObject.LociList_Rules
anyLocusValueExists, prospectiveOffspringGenomesList, err := getProspectiveOffspringGenomesList(traitLociList, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return false, 0, nil, nil, err }
anyLocusValueExists, prospectiveOffspringGenomesList, err := getProspectiveOffspringGenomesList(traitLociList_Rules, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return false, false, 0, 0, nil, nil, err }
if (anyLocusValueExists == false){
return false, 0, nil, nil, nil
return true, false, 0, 0, nil, nil, nil
}
traitRulesList := traitObject.RulesList
// Map Structure: Rule Identifier -> Number of offspring who pass the rule (out of 100 prospective offspring)
// Because there are 100 offspring, this also represents the percentage probability that an offspring will pass the rule
offspringPassesRulesCountMap := make(map[[3]byte]int)
// We use this map to keep track of the rules for which we know every offspring's passes-rule status
// Map Structure: Rule Identifier -> Rule Object
offspringRulesWithKnownStatusMap := make(map[[3]byte]traits.TraitRule)
// This map stores the quantity of offspring who have each outcome
// The probability an offspring will have this outcome is the same as the
// quantity of offspring who have this outcome in our set of 100 randomly generated offspring
// Map structure: Outcome name -> quantity of offspring who have this outcome
outcomeCountsMap := make(map[string]int)
for offspringIndex, offspringGenomeMap := range prospectiveOffspringGenomesList{
quantityOfLociKnown := 0
// We iterate through rules to determine genome pair trait info
for index, offspringGenomeMap := range prospectiveOffspringGenomesList{
for _, ruleObject := range traitRulesList{
// Now we get outcome prediction for prospective offspring
ruleIdentifierHex := ruleObject.RuleIdentifier
anyRulesExist, quantityOfRulesTested, currentQuantityOfLociKnown, offspringPassesRulesMap, predictionOutcomeIsKnown, predictedOutcome, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_Rules(traitObject, offspringGenomeMap, false)
if (err != nil) { return false, false, 0, 0, nil, nil, err }
if (anyRulesExist == false){
return false, false, 0, 0, nil, nil, errors.New("GetGenomeTraitAnalysis_Rules returning noRulesExists when we already checked and trait rules do in-fact exist.")
}
if (quantityOfRulesTested == 0){
// This will be the same for each of the 100 generated offspring
// No analysis is possible.
return true, false, 0, currentQuantityOfLociKnown, nil, nil, nil
}
if (index == 0){
// currentQuantityOfLociKnown will be the same for each prospective offspring
quantityOfLociKnown = currentQuantityOfLociKnown
}
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return false, 0, nil, nil, err }
if (offspringIndex == 0){
offspringRulesWithKnownStatusMap[ruleIdentifier] = ruleObject
} else {
_, exists := offspringRulesWithKnownStatusMap[ruleIdentifier]
if (exists == false){
// We already tried to check a previous offspring's passes-rule status for this rule
// We know that the offspring's passes-rule status will be unknown for every prospective offspring
continue
}
}
// This is a list that describes the locus rsids and their values that must be fulfilled to pass the rule
ruleLocusObjectsList := ruleObject.LociList
offspringPassesRuleIsKnown, offspringPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLocusObjectsList, offspringGenomeMap, false)
if (err != nil){ return false, 0, nil, nil, err }
if (offspringPassesRuleIsKnown == false){
continue
}
if (offspringPassesRule == true){
for ruleIdentifier, genomePassesRule := range offspringPassesRulesMap{
if (genomePassesRule == true){
offspringPassesRulesCountMap[ruleIdentifier] += 1
}
}
}
// Map Structure: Rule Identifier -> Offspring Probability Of Passing Rule
// The map value stores the probability that the offspring will pass the rule
// This is a number between 0-100%
offspringProbabilityOfPassingRulesMap := make(map[[3]byte]int)
// Map Structure: Outcome Name -> Outcome Score
// Example: "Intolerant" -> 2.5
offspringAverageOutcomeScoresMap := make(map[string]float64)
for ruleIdentifier, ruleObject := range offspringRulesWithKnownStatusMap{
//Output:
// -int: Offspring probability of passing rule (0-100%)
getOffspringPercentageProbabilityOfPassingRule := func()int{
numberOfOffspringWhoPassRule, exists := offspringPassesRulesCountMap[ruleIdentifier]
if (exists == false){
// None of the offspring passed the rule
return 0
}
// There are 100 tested offspring
// Thus, the percentage of offspring who passed the rule is the same as the number of offspring who passed the rule
// The probability of the offspring passing the rule is the same as the percentage of offspring who passed the rule
return numberOfOffspringWhoPassRule
if (predictionOutcomeIsKnown == false){
// There was a tie between outcomes for this offspring
// We can't predict anything about this trait for this couple using rules
// This is why we need to create rules which make it unlikely for a tie between outcomes to occur.
return true, false, 0, 0, nil, nil, nil
}
offspringPercentageProbabilityOfPassingRule := getOffspringPercentageProbabilityOfPassingRule()
offspringProbabilityOfPassingRulesMap[ruleIdentifier] = offspringPercentageProbabilityOfPassingRule
// This is the 0 - 1 probability value
offspringProbabilityOfPassingRule := float64(offspringPercentageProbabilityOfPassingRule)/100
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
for outcomeName, outcomePointsEffect := range ruleOutcomePointsMap{
pointsToAdd := float64(outcomePointsEffect) * offspringProbabilityOfPassingRule
offspringAverageOutcomeScoresMap[outcomeName] += pointsToAdd
}
outcomeCountsMap[predictedOutcome] += 1
}
numberOfRulesTested := len(offspringProbabilityOfPassingRulesMap)
quantityOfRulesTested := len(offspringPassesRulesCountMap)
if (numberOfRulesTested == 0){
return false, 0, nil, nil, nil
}
traitOutcomesList := traitObject.OutcomesList
// We add all outcomes for which there were no points
for _, traitOutcome := range traitOutcomesList{
_, exists := offspringAverageOutcomeScoresMap[traitOutcome]
if (exists == false){
offspringAverageOutcomeScoresMap[traitOutcome] = 0
}
}
return true, numberOfRulesTested, offspringProbabilityOfPassingRulesMap, offspringAverageOutcomeScoresMap, nil
return true, true, quantityOfRulesTested, quantityOfLociKnown, offspringPassesRulesCountMap, outcomeCountsMap, nil
}
@ -1281,6 +1307,10 @@ func GetOffspringTraitInfo(traitObject traits.Trait, person1LocusValuesMap map[i
// Each genome represents an equal-probability offspring genome from both people's genomes
// This function takes into account the effects of genetic linkage
// Any locations which do not exist in both people's genomes will not be included
//
// TODO: The user should be able to choose how many prospective offspring to create in the settings.
// More offspring will take longer, but will yield a more accurate trait analysis.
//
//Outputs:
// -bool: Any locus value exists between both users
// -[]map[int64]locusValue.LocusValue
@ -1388,7 +1418,7 @@ func getProspectiveOffspringGenomesList(lociList []int64, person1LociMap map[int
// We step by 1,000,000 each time
// It would be more realistic if we did it in 1 integer increments, but it would be slower
for position := int64(0); position <= chromosomeLength; position += 1000000{
for position := int64(0); position < chromosomeLength; position += 1_000_000{
//From Wikipedia:
// A centimorgan (abbreviated cM) is a unit for measuring genetic linkage.
@ -1441,14 +1471,19 @@ func getProspectiveOffspringGenomesList(lociList []int64, person1LociMap map[int
}
personLocusBase1 := personLocusValue.Base1Value
personLocusBase2 := personLocusValue.Base1Value
personLocusBase2 := personLocusValue.Base2Value
personLocusIsPhased := personLocusValue.LocusIsPhased
if (personLocusBase1 == personLocusBase2){
// Phase doesn't matter
return true, personLocusBase1, nil
}
if (personLocusIsPhased == false){
// Breakpoints are unnecessary
// We either choose base 1 or 2
randomBool := helpers.GetRandomBool()
if (randomBool == true){
randomInt := pseudorandomNumberGenerator.IntN(2)
if (randomInt == 1){
return true, personLocusBase1, nil
}
return true, personLocusBase2, nil
@ -1490,9 +1525,9 @@ func getProspectiveOffspringGenomesList(lociList []int64, person1LociMap map[int
getLocusListIndex := func()int{
for index, breakpoint := range personBreakpointsList{
for index, breakpointPosition := range personBreakpointsList{
if (int64(locusPosition) <= breakpoint){
if (int64(locusPosition) <= breakpointPosition){
return index
}

View file

@ -22,13 +22,14 @@ import "seekia/resources/geneticReferences/traits"
import "seekia/internal/encoding"
import "seekia/internal/genetics/geneticAnalysis"
import "seekia/internal/genetics/geneticPrediction"
import "seekia/internal/genetics/locusValue"
import "seekia/internal/genetics/prepareRawGenomes"
import "seekia/internal/helpers"
import "errors"
import "slices"
import "maps"
import "reflect"
//Outputs:
@ -51,10 +52,23 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe
genomesWithMetadataList, allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(genomesList, prepareRawGenomesUpdatePercentageCompleteFunction)
if (err != nil) { return false, "", err }
// This map stores each genome's locus values
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
genomesMap := make(map[[16]byte]map[int64]locusValue.LocusValue)
for _, genomeWithMetadata := range genomesWithMetadataList{
genomeIdentifier := genomeWithMetadata.GenomeIdentifier
genomeMap := genomeWithMetadata.GenomeMap
genomesMap[genomeIdentifier] = genomeMap
}
newGeneticAnalysisObject := geneticAnalysis.PersonAnalysis{
AnalysisVersion: 1,
CombinedGenomesExist: multipleGenomesExist,
AllRawGenomeIdentifiersList: allRawGenomeIdentifiersList,
GenomesMap: genomesMap,
}
if (multipleGenomesExist == true){
@ -107,20 +121,30 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return false, "", err }
// Map Structure: Trait Name -> PersonTraitInfo
analysisTraitsMap := make(map[string]geneticAnalysis.PersonTraitInfo)
// This map will always contain an entry for each discrete trait
// Map Structure: Trait Name -> PersonDiscreteTraitInfo
analysisDiscreteTraitsMap := make(map[string]geneticAnalysis.PersonDiscreteTraitInfo)
// This map will not contain entries for traits which this person's genome has no known loci
// Map Structure: Trait Name -> PersonNumericTraitInfo
analysisNumericTraitsMap := make(map[string]geneticAnalysis.PersonNumericTraitInfo)
for _, traitObject := range traitObjectsList{
personTraitAnalysisObject, err := GetPersonTraitAnalysis(genomesWithMetadataList, traitObject)
if (err != nil) { return false, "", err }
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
analysisTraitsMap[traitName] = personTraitAnalysisObject
if (traitIsDiscreteOrNumeric == "Discrete"){
personTraitAnalysisObject, err := GetPersonDiscreteTraitAnalysis(genomesWithMetadataList, traitObject)
if (err != nil) { return false, "", err }
analysisDiscreteTraitsMap[traitName] = personTraitAnalysisObject
}
}
newGeneticAnalysisObject.TraitsMap = analysisTraitsMap
newGeneticAnalysisObject.DiscreteTraitsMap = analysisDiscreteTraitsMap
newGeneticAnalysisObject.NumericTraitsMap = analysisNumericTraitsMap
analysisBytes, err := encoding.EncodeMessagePackBytes(newGeneticAnalysisObject)
if (err != nil) { return false, "", err }
@ -505,9 +529,9 @@ func GetPersonMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw
diseaseAnalysisObject := geneticAnalysis.PersonGenomeMonogenicDiseaseInfo{
PersonHasDisease: personHasDisease,
NumberOfVariantsTested: numberOfVariantsTested,
NumberOfLociTested: numberOfLociTested,
NumberOfPhasedLoci: numberOfPhasedLoci,
QuantityOfVariantsTested: numberOfVariantsTested,
QuantityOfLociTested: numberOfLociTested,
QuantityOfPhasedLoci: numberOfPhasedLoci,
ProbabilityOfPassingADiseaseVariant: percentageProbabilityPersonWillPassADiseaseVariant,
VariantsInfoMap: variantsInfoMap,
}
@ -737,16 +761,15 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw
genomeLocusValuesMap[locusRSID] = locusValueObject
}
anyLociTested, personDiseaseRiskScore, genomeNumberOfLociTested, genomeLociInfoMap, err := GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, genomeLocusValuesMap, false)
anyLociTested, personDiseaseRiskScore, genomeNumberOfLociTested, genomeLociInfoMap, err := GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, genomeLocusValuesMap, true)
if (err != nil) { return emptyDiseaseInfoObject, err }
if (anyLociTested == false){
continue
}
newDiseaseInfoObject := geneticAnalysis.PersonGenomePolygenicDiseaseInfo{
NumberOfLociTested: genomeNumberOfLociTested,
QuantityOfLociTested: genomeNumberOfLociTested,
RiskScore: personDiseaseRiskScore,
LocusValuesMap: genomeLocusValuesMap,
LociInfoMap: genomeLociInfoMap,
}
@ -777,7 +800,7 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw
for _, personGenomeDiseaseInfoObject := range personPolygenicDiseaseInfoMap{
currentGenomeRiskScore := personGenomeDiseaseInfoObject.RiskScore
currentGenomeNumberOfLociTested := personGenomeDiseaseInfoObject.NumberOfLociTested
currentGenomeNumberOfLociTested := personGenomeDiseaseInfoObject.QuantityOfLociTested
if (firstItemReached == false){
genomeRiskScore = currentGenomeRiskScore
@ -855,106 +878,68 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw
//Outputs:
// -geneticAnalysis.PersonTraitInfo: Trait analysis object
// -geneticAnalysis.PersonDiscreteTraitInfo: Trait analysis object
// -error
func GetPersonTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, traitObject traits.Trait)(geneticAnalysis.PersonTraitInfo, error){
func GetPersonDiscreteTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, traitObject traits.Trait)(geneticAnalysis.PersonDiscreteTraitInfo, error){
// We use this when returning errors
emptyPersonTraitInfo := geneticAnalysis.PersonTraitInfo{}
traitLociList := traitObject.LociList
traitRulesList := traitObject.RulesList
// Map Structure: Genome Identifier -> PersonGenomeTraitInfo
newPersonTraitInfoMap := make(map[[16]byte]geneticAnalysis.PersonGenomeTraitInfo)
// Map Structure: Genome Identifier -> PersonGenomeDiscreteTraitInfo
newPersonTraitInfoMap := make(map[[16]byte]geneticAnalysis.PersonGenomeDiscreteTraitInfo)
for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{
genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier
genomeMap := genomeWithMetadataObject.GenomeMap
// This map contains the locus values for the genome
// If an locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
genomeLocusValuesMap := make(map[int64]locusValue.LocusValue)
newPersonGenomeTraitInfo := geneticAnalysis.PersonGenomeDiscreteTraitInfo{}
for _, locusRSID := range traitLociList{
neuralNetworkExists, neuralNetworkOutcomeIsKnown, predictedOutcome, predictionConfidence, quantityOfLociTested, quantityOfPhasedLoci, err := GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, genomeMap, true)
if (err != nil) { return geneticAnalysis.PersonDiscreteTraitInfo{}, err }
if (neuralNetworkExists == true){
locusBasePairKnown, _, _, _, locusValueObject, err := GetLocusValueFromGenomeMap(true, genomeMap, locusRSID)
if (err != nil) { return emptyPersonTraitInfo, err }
if (locusBasePairKnown == false){
continue
}
newPersonGenomeTraitInfo.NeuralNetworkExists = true
genomeLocusValuesMap[locusRSID] = locusValueObject
}
if (neuralNetworkOutcomeIsKnown == true){
// This map contains the trait outcome scores for the genome
// Map Structure: Outcome Name -> Score
// Example: "Intolerant" -> 5
traitOutcomeScoresMap := make(map[string]int)
newPersonGenomeTraitInfo.NeuralNetworkAnalysisExists = true
// Map Structure: Rule Identifier -> Genome Passes rule (true if the genome passes the rule)
personPassesRulesMap := make(map[[3]byte]bool)
newPersonGenomeTraitInfo_NeuralNetwork := geneticAnalysis.PersonGenomeDiscreteTraitInfo_NeuralNetwork{
if (len(traitRulesList) != 0){
// At least 1 rule exists for this trait
for _, ruleObject := range traitRulesList{
ruleIdentifierHex := ruleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return emptyPersonTraitInfo, err }
ruleLociList := ruleObject.LociList
genomePassesRuleIsKnown, genomePassesRule, err := GetGenomePassesTraitRuleStatus(ruleLociList, genomeMap, false)
if (err != nil) { return emptyPersonTraitInfo, err }
if (genomePassesRuleIsKnown == false){
continue
PredictedOutcome: predictedOutcome,
PredictionConfidence: predictionConfidence,
QuantityOfLociKnown: quantityOfLociTested,
QuantityOfPhasedLoci: quantityOfPhasedLoci,
}
personPassesRulesMap[ruleIdentifier] = genomePassesRule
newPersonGenomeTraitInfo.NeuralNetworkAnalysis = newPersonGenomeTraitInfo_NeuralNetwork
}
}
// The rule has been passed by this genome
// We add the outcome points for the rule to the traitOutcomeScoresMap
anyRulesExist, quantityOfRulesTested, quantityOfLociKnown, genomePassesRulesMap, predictedOutcomeExists, predictedOutcome, err := GetGenomeDiscreteTraitAnalysis_Rules(traitObject, genomeMap, true)
if (err != nil) { return geneticAnalysis.PersonDiscreteTraitInfo{}, err }
if (anyRulesExist == true){
newPersonGenomeTraitInfo.AnyRulesExist = true
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
if (quantityOfRulesTested != 0){
for traitOutcome, pointsChange := range ruleOutcomePointsMap{
newPersonGenomeTraitInfo.RulesAnalysisExists = true
traitOutcomeScoresMap[traitOutcome] += pointsChange
newPersonGenomeTraitInfo_Rules := geneticAnalysis.PersonGenomeDiscreteTraitInfo_Rules{
GenomePassesRulesMap: genomePassesRulesMap,
PredictedOutcomeExists: predictedOutcomeExists,
PredictedOutcome: predictedOutcome,
QuantityOfRulesTested: quantityOfRulesTested,
QuantityOfLociKnown: quantityOfLociKnown,
}
newPersonGenomeTraitInfo.RulesAnalysis = newPersonGenomeTraitInfo_Rules
}
}
traitOutcomesList := traitObject.OutcomesList
// We add all outcomes for which there were no points
for _, traitOutcome := range traitOutcomesList{
_, exists := traitOutcomeScoresMap[traitOutcome]
if (exists == false){
traitOutcomeScoresMap[traitOutcome] = 0
}
}
numberOfRulesTested := len(personPassesRulesMap)
newPersonGenomeTraitInfo := geneticAnalysis.PersonGenomeTraitInfo{
NumberOfRulesTested: numberOfRulesTested,
LocusValuesMap: genomeLocusValuesMap,
OutcomeScoresMap: traitOutcomeScoresMap,
GenomePassesRulesMap: personPassesRulesMap,
}
newPersonTraitInfoMap[genomeIdentifier] = newPersonGenomeTraitInfo
}
newPersonTraitInfoObject := geneticAnalysis.PersonTraitInfo{
newPersonTraitInfoObject := geneticAnalysis.PersonDiscreteTraitInfo{
TraitInfoMap: newPersonTraitInfoMap,
}
@ -968,40 +953,20 @@ func GetPersonTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.Gen
getConflictExistsBool := func()(bool, error){
//TODO: Check for locus value conflicts once locus values are used in neural network prediction.
if (len(traitRulesList) == 0){
return false, nil
}
// We check to see if the outcome scores are the same for all genomes
// We also check each rule result
// We check to see if the analysis results are the same for all genomes
firstItemReached := false
outcomeScoresMap := make(map[string]int)
passesRulesMap := make(map[[3]byte]bool)
personGenomeTraitInfoObject := geneticAnalysis.PersonGenomeDiscreteTraitInfo{}
for _, genomeTraitInfoObject := range newPersonTraitInfoMap{
currentGenomeOutcomeScoresMap := genomeTraitInfoObject.OutcomeScoresMap
currentGenomePassesRulesMap := genomeTraitInfoObject.GenomePassesRulesMap
if (firstItemReached == false){
outcomeScoresMap = currentGenomeOutcomeScoresMap
passesRulesMap = currentGenomePassesRulesMap
firstItemReached = true
personGenomeTraitInfoObject = genomeTraitInfoObject
continue
}
areEqual := maps.Equal(currentGenomeOutcomeScoresMap, outcomeScoresMap)
areEqual := reflect.DeepEqual(personGenomeTraitInfoObject, genomeTraitInfoObject)
if (areEqual == false){
// A conflict exists
return true, nil
}
areEqual = maps.Equal(currentGenomePassesRulesMap, passesRulesMap)
if (areEqual == false){
// A conflict exists
return true, nil
}
}
@ -1010,7 +975,7 @@ func GetPersonTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.Gen
}
conflictExists, err := getConflictExistsBool()
if (err != nil) { return emptyPersonTraitInfo, err }
if (err != nil) { return geneticAnalysis.PersonDiscreteTraitInfo{}, err }
newPersonTraitInfoObject.ConflictExists = conflictExists
@ -1046,12 +1011,205 @@ func GetGenomePolygenicDiseaseLocusRiskInfo(locusRiskWeightsMap map[string]int,
return riskWeight, true, oddsRatio, nil
}
// We use this to generate trait predictions using a neural network
// The alternative prediction method is to use Rules (see GetGenomeTraitAnalysis_Rules)
//Outputs:
// -bool: Trait Neural network analysis available (if false, we can't predict this trait using a neural network)
// -bool: Neural network outcome is known (at least 1 locus value is known which is needed for the neural network
// -string: The predicted outcome (Example: "Blue")
// -int: Probability (0-100) that the outcome from neural network is true (confidence)
// -int: Quantity of loci tested
// -int: Quantity of phased loci
// -error
func GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject traits.Trait, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, bool, string, int, int, int, error){
getGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (checkForAliases == false){
// We don't need to check for rsID aliases.
return genomeMap, nil
}
traitLociList := traitObject.LociList
// This map contains the locus values for the genome
// If a locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
genomeLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, locusRSID := range traitLociList{
locusBasePairKnown, _, _, _, locusValueObject, err := GetLocusValueFromGenomeMap(checkForAliases, genomeMap, locusRSID)
if (err != nil) { return nil, err }
if (locusBasePairKnown == false){
continue
}
genomeLocusValuesMap[locusRSID] = locusValueObject
}
return genomeLocusValuesMap, nil
}
genomeLocusValuesMap, err := getGenomeLocusValuesMap()
if (err != nil) { return false, false, "", 0, 0, 0, err }
traitName := traitObject.TraitName
neuralNetworkModelExists, traitPredictionIsPossible, predictedOutcome, predictionConfidence, quantityOfLociKnown, quantityOfPhasedLoci, err := geneticPrediction.GetNeuralNetworkTraitPredictionFromGenomeMap(traitName, genomeLocusValuesMap)
if (err != nil) { return false, false, "", 0, 0, 0, err }
if (neuralNetworkModelExists == false){
return false, false, "", 0, 0, 0, nil
}
if (traitPredictionIsPossible == false){
return true, false, "", 0, 0, 0, nil
}
return true, true, predictedOutcome, predictionConfidence, quantityOfLociKnown, quantityOfPhasedLoci, nil
}
//Outputs:
// -bool: Rule-based trait prediction is available
// -int: Quantity of trait rules tested
// -int: Quantity of loci known
// -map[[3]byte]bool: Passed rules map (Rule Identifier -> Genome passes rule)
// -bool: Rule-based prediction outcome is known (at least 1 rule has been tested and there is no outcome tie)
// -string: The predicted outcome (Example: "Tolerant")
// -error
func GetGenomeDiscreteTraitAnalysis_Rules(traitObject traits.Trait, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, int, int, map[[3]byte]bool, bool, string, error){
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
return false, 0, 0, nil, false, "", errors.New("GetGenomeDiscreteTraitAnalysis_Rules called with non-discrete trait.")
}
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
// We can't predict this trait using rules
// This means that neural network prediction is the only alternative potential way to predict this trait
return false, 0, 0, nil, false, "", nil
}
// This map contains the trait outcome scores for the genome
// Map Structure: Outcome Name -> Score
// Example: "Intolerant" -> 5
traitOutcomeScoresMap := make(map[string]int)
// Map Structure: Rule Identifier -> Genome Passes rule (true if the genome passes the rule)
personPassesRulesMap := make(map[[3]byte]bool)
// This map stores each known loci
// Multiple rules can use the same loci, so we need a map to avoid duplicates
// Map Structure: Locus RSID -> Locus is known
lociAreKnownMap := make(map[int64]bool)
for _, ruleObject := range traitRulesList{
ruleIdentifierHex := ruleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return false, 0, 0, nil, false, "", err }
ruleLociList := ruleObject.LociList
for _, locusObject := range ruleLociList{
locusRSID := locusObject.LocusRSID
_, exists := lociAreKnownMap[locusRSID]
if (exists == true){
// We already know if this locus is known
continue
}
locusIsKnown, _, _, _, _, err := GetLocusValueFromGenomeMap(checkForAliases, genomeMap, locusRSID)
if (err != nil) { return false, 0, 0, nil, false, "", err }
lociAreKnownMap[locusRSID] = locusIsKnown
}
genomePassesRuleIsKnown, genomePassesRule, err := GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, genomeMap, checkForAliases)
if (err != nil) { return false, 0, 0, nil, false, "", err }
if (genomePassesRuleIsKnown == false){
continue
}
personPassesRulesMap[ruleIdentifier] = genomePassesRule
// The rule has been passed by this genome
// We add the outcome points for the rule to the traitOutcomeScoresMap
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
for traitOutcome, pointsChange := range ruleOutcomePointsMap{
traitOutcomeScoresMap[traitOutcome] += pointsChange
}
}
quantityOfLociKnown := 0
for _, locusIsKnown := range lociAreKnownMap{
if (locusIsKnown == true){
quantityOfLociKnown += 1
}
}
quantityOfRulesTested := len(personPassesRulesMap)
if (quantityOfRulesTested == 0){
return true, 0, quantityOfLociKnown, personPassesRulesMap, false, "", nil
}
// -bool: Outcome is known (will be false if there is a tie
// -string:
// -error
getOutcome := func()(bool, string, error){
largestOutcome := ""
largestOutcomePoints := 0
tieExists := false
for outcomeName, outcomePoints := range traitOutcomeScoresMap{
if (outcomePoints < 1){
// This should never happen, because outcomes points should only be increased by integers which are at least 1 or greater
return false, "", errors.New("traitOutcomeScoresMap contains outcomePoints < 1.")
}
if (outcomePoints > largestOutcomePoints){
largestOutcome = outcomeName
largestOutcomePoints = outcomePoints
tieExists = false
continue
} else if (outcomePoints == largestOutcomePoints){
tieExists = true
}
}
if (tieExists == true){
return false, "", nil
}
return true, largestOutcome, nil
}
outcomeIsKnown, outcomeName, err := getOutcome()
if (err != nil) { return false, 0, 0, nil, false, "", err }
if (outcomeIsKnown == false){
return true, quantityOfRulesTested, quantityOfLociKnown, personPassesRulesMap, false, "", nil
}
return true, quantityOfRulesTested, quantityOfLociKnown, personPassesRulesMap, true, outcomeName, nil
}
// This function checks to see if a genome will pass a trait rule
// Outputs:
// -bool: Genome passes trait rule status is known
// -bool: Genome passes trait rule
// -error
func GetGenomePassesTraitRuleStatus(ruleLociList []traits.RuleLocus, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, bool, error){
func GetGenomePassesDiscreteTraitRuleStatus(ruleLociList []traits.RuleLocus, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, bool, error){
// We check to see if genome passes all rule loci
// To pass a rule, all of the rule's loci must be passed by the provided genome

View file

@ -22,14 +22,27 @@ type PersonAnalysis struct{
OnlyIncludeSharedGenomeIdentifier [16]byte
OnlyExcludeConflictsGenomeIdentifier [16]byte
// This map stores each genome's locus values
// Only the loci that belong in the locusMetadata package are inside of this map
// This is necessary, otherwise genetic analyses would be too large by containing each analyzed raw genome.
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
GenomesMap map[[16]byte]map[int64]locusValue.LocusValue
// Map Structure: Disease Name -> PersonMonogenicDiseaseInfo
MonogenicDiseasesMap map[string]PersonMonogenicDiseaseInfo
// Map Structure: Disease Name -> PersonPolygenicDiseaseInfo
PolygenicDiseasesMap map[string]PersonPolygenicDiseaseInfo
// These are traits which have discrete outcomes, rather than numeric outcomes
// For example: Eye color
// Map Structure: Trait Name -> Trait Info Object
TraitsMap map[string]PersonTraitInfo
DiscreteTraitsMap map[string]PersonDiscreteTraitInfo
// These are traits which have numeric outcomes, rather than discrete outcomes
// For example: Height
// Map Structure: Trait Name -> Trait Info Object
NumericTraitsMap map[string]PersonNumericTraitInfo
}
@ -50,15 +63,15 @@ type PersonGenomeMonogenicDiseaseInfo struct{
PersonHasDisease bool
// This describes the number of variants that were tested for this disease
NumberOfVariantsTested int
QuantityOfVariantsTested int
// This describes the number of loci that were tested for this disease
// 1 locus can have multiple potential variants
NumberOfLociTested int
QuantityOfLociTested int
// This describes the number of loci which are phased
// This number will always be <= NumberOfLociTested
NumberOfPhasedLoci int
// This number will always be <= QuantityOfLociTested
QuantityOfPhasedLoci int
// This describes the probability that the person will pass a disease variant
// It is a value that represents a percentage between 0-100
@ -94,14 +107,9 @@ type PersonPolygenicDiseaseInfo struct{
type PersonGenomePolygenicDiseaseInfo struct{
// This describes the number of loci tested for this disease
// This should be len(LociInfoList)
NumberOfLociTested int
// This map contains the locus values for the genome for this trait
// If an locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
LocusValuesMap map[int64]locusValue.LocusValue
// This describes the quantity of loci tested for this disease
// This should be len(LociInfoMap)
QuantityOfLociTested int
// This is total risk score for this disease for the person's genome
// This is a number between 1-10
@ -128,34 +136,102 @@ type PersonGenomePolygenicDiseaseLocusInfo struct{
}
type PersonTraitInfo struct{
type PersonDiscreteTraitInfo struct{
// This map contains the person's trait info for each genome
// If no map entries exist, then no trait info is known
// Map Structure: Genome Identifier -> PersonGenomeTraitInfo
TraitInfoMap map[[16]byte]PersonGenomeTraitInfo
// Map Structure: Genome Identifier -> PersonGenomeDiscreteTraitInfo
TraitInfoMap map[[16]byte]PersonGenomeDiscreteTraitInfo
// This is true if there are multiple genomes and the results from each genome differ
ConflictExists bool
}
type PersonGenomeTraitInfo struct{
// For a trait analysis, both analysis methods may exist in the results
// However, the GUI will only display the results from one of the methods.
// The neural network prediction is always prioritized over the rule-based prediction
type PersonGenomeDiscreteTraitInfo struct{
// This should be len(GenomePassesRulesMap)
NumberOfRulesTested int
// This is true if it is possible to analyze this trait using a neural network
NeuralNetworkExists bool
// This map contains the locus values for the genome for this trait
// If an locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
LocusValuesMap map[int64]locusValue.LocusValue
// This is true if a neural network analysis was performed for this genome
// This means that at least 1 locus for this trait was contained in the genome
NeuralNetworkAnalysisExists bool
// This map contains the outcome scores for the genome
// Map Structure: Outcome Name -> Score
// Example: "Intolerant" -> 5
OutcomeScoresMap map[string]int
NeuralNetworkAnalysis PersonGenomeDiscreteTraitInfo_NeuralNetwork
// This is true if it is possible to analyze this trait using rules
AnyRulesExist bool
// This is true if a rules-based analysis was performed for this genome
// This means that all of the loci for at least 1 rule for this trait was contained in the genome
RulesAnalysisExists bool
RulesAnalysis PersonGenomeDiscreteTraitInfo_Rules
}
type PersonGenomeDiscreteTraitInfo_NeuralNetwork struct{
// The predicted outcome (Example: "Blue")
PredictedOutcome string
// Probability (0-100) that the outcome from the neural network is true
PredictionConfidence int
QuantityOfLociKnown int
QuantityOfPhasedLoci int
}
type PersonGenomeDiscreteTraitInfo_Rules struct{
// Map Structure: Rule Identifier -> Genome Passes rule (true if the genome passes the rule)
GenomePassesRulesMap map[[3]byte]bool
// This is true if there was not a tie between summed rule outcome scores
// It is possible to have some tested rules without a known outcome
PredictedOutcomeExists bool
// This is the outcome that was predicted
// Example: "Intolerant"
PredictedOutcome string
// This should be len(GenomePassesRulesMap)
QuantityOfRulesTested int
// This only counts the loci which are used for rules
// For example, loci that are only used in neural-network-based prediction are not counted
QuantityOfLociKnown int
}
type PersonNumericTraitInfo struct{
// This map contains the person's trait info for each genome
// If no map entries exist, then no trait info is known
// Map Structure: Genome Identifier -> PersonGenomeNumericTraitInfo
TraitInfoMap map[[16]byte]PersonGenomeNumericTraitInfo
// This is true if there are multiple genomes and the results from each genome differ
ConflictExists bool
}
type PersonGenomeNumericTraitInfo struct{
// The predicted outcome (Example: The predicted height for this person, in centimeters)
PredictedOutcome float64
// This map stores the confidence ranges for the predicted value
// If we want to know how accurate the prediction is with a X% accuracy, how far would we have to expand the
// predicted value's range to be accurate, X% of the time?
// For example: 50% accuracy requires a +/-5 point range, 80% accuracy requires a +-15 point range
// Map Structure: Accuracy probability (0-100) -> Amount to add to value in both +/- directions so prediction is that accurate
ConfidenceRangesMap map[int]float64
QuantityOfLociKnown int
QuantityOfPhasedLoci int
}
@ -190,16 +266,21 @@ type CoupleAnalysis struct{
// Map Structure: Disease Name -> OffspringPolygenicDiseaseInfo
PolygenicDiseasesMap map[string]OffspringPolygenicDiseaseInfo
// Discrete traits are traits with discrete outcomes, such as Eye Color
// Map Structure: Trait Name -> Trait Info Object
TraitsMap map[string]OffspringTraitInfo
DiscreteTraitsMap map[string]OffspringDiscreteTraitInfo
// Numeric traits are traits with numeric outcomes, such as Height
// Map Structure: Trait Name -> Trait Info Object
NumericTraitsMap map[string]OffspringNumericTraitInfo
}
type OffspringMonogenicDiseaseInfo struct{
// This map stores the number of variants tested in each person's genome
// This map stores the quantity of variants tested in each person's genome
// Map Structure: Genome Identifier -> Number of variants tested
NumberOfVariantsTestedMap map[[16]byte]int
QuantityOfVariantsTestedMap map[[16]byte]int
// This map stores the offspring disease probabilities for each genome pair.
// A genome pair is a concatenation of two genome identifiers
@ -215,7 +296,6 @@ type OffspringMonogenicDiseaseInfo struct{
type OffspringGenomePairMonogenicDiseaseInfo struct{
// At least 1 variant's information is needed from either person to include the diseaseInfo object in the MonogenicDiseaseInfoMap
ProbabilityOffspringHasDiseaseIsKnown bool
// This is the probability that the offspring will have the disease
@ -259,8 +339,8 @@ type OffspringPolygenicDiseaseInfo struct{
type OffspringGenomePairPolygenicDiseaseInfo struct{
// This should be len(DiseaseLociList)
NumberOfLociTested int
// This should be len(LociInfoMap)
QuantityOfLociTested int
// A number between 1-10 representing the offspring's average risk score
// 1 == lowest risk, 10 == highest risk
@ -297,27 +377,110 @@ type OffspringPolygenicDiseaseLocusInfo struct{
}
type OffspringTraitInfo struct{
type OffspringDiscreteTraitInfo struct{
// This map stores the trait info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairTraitInfo
TraitInfoMap map[[32]byte]OffspringGenomePairTraitInfo
TraitInfoMap map[[32]byte]OffspringGenomePairDiscreteTraitInfo
ConflictExists bool
}
type OffspringGenomePairTraitInfo struct{
// For a trait analysis, both analysis methods may exist in the results
// However, the GUI will only display the results from one of the methods.
// The neural network prediction is always prioritized over the rule-based prediction
type OffspringGenomePairDiscreteTraitInfo struct{
// This should be len(TraitRulesList)
NumberOfRulesTested int
// This is true if it is possible to analyze this trait using a neural network
NeuralNetworkExists bool
// Map Structure: Outcome Name -> Outcome Score
// Example: "Intolerant" -> 2.5
OffspringAverageOutcomeScoresMap map[string]float64
// This is true if a neural network analysis was performed for this genome
// This means that at least 1 locus for this trait was contained in both of the genomes in the pair
NeuralNetworkAnalysisExists bool
NeuralNetworkAnalysis OffspringGenomePairDiscreteTraitInfo_NeuralNetwork
// This is true if it is possible to analyze this trait using rules
RulesExist bool
// This is true if a rules-based analysis was performed for this genome
// This means that all of the loci for at least 1 rule for this trait was contained in both of the genomes in the pair
// Also, none of the offspring have an unknown outcome caused by an outcome score tie
RulesAnalysisExists bool
RulesAnalysis OffspringGenomePairDiscreteTraitInfo_Rules
}
type OffspringGenomePairDiscreteTraitInfo_NeuralNetwork struct{
// Map Structure: Outcome Name -> Outcome Probability (0-100)
// Example: "Intolerant" -> 5
OffspringOutcomeProbabilitiesMap map[string]int
// Probability (0-100) that each outcome from the neural network is true
// This is an average of the confidence for each of the calculated 100 outcome probabilities
AverageConfidence int
QuantityOfLociKnown int
// This describes the quantity of loci from both parents that are phased
// For example, if there are 10 loci for this trait, and one parent has 10 phased loci and the other has 5,
// this variable will have a value of 15
QuantityOfParentalPhasedLoci int
}
type OffspringGenomePairDiscreteTraitInfo_Rules struct{
// Map Structure: Outcome Name -> Outcome Probability (0-100)
// Example: "Intolerant" -> 5
OffspringOutcomeProbabilitiesMap map[string]int
// Map Structure: Rule Identifier -> Offspring Probability Of Passing Rule
// The value stores the probability that the offspring will pass the rule
// This is a number between 0-100%
ProbabilityOfPassingRulesMap map[[3]byte]int
// This should be len(ProbabilityOfPassingRulesMap)
QuantityOfRulesTested int
// This only counts the loci which are used for rules
// For example, loci that are only used in neural-network-based prediction are not counted
QuantityOfLociKnown int
}
type OffspringNumericTraitInfo struct{
// This map stores the trait info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairNumericTraitInfo
TraitInfoMap map[[32]byte]OffspringGenomePairNumericTraitInfo
ConflictExists bool
}
type OffspringGenomePairNumericTraitInfo struct{
// The average outcome for the offspring
// For example, the average height for an offspring between these 2 people
OffspringAverageOutcome float64
// This map stores the confidence ranges for the predicted value
// If we want to know how accurate the prediction is with a X% accuracy, how far would we have to expand the
// predicted value's range to be accurate, X% of the time?
// For example: 50% accuracy requires a +/-5 point range, 80% accuracy requires a +-15 point range
// Map Structure: Accuracy probability (0-100) -> Amount to add to value in both +/- directions so prediction is that accurate
AverageConfidenceRangesMap map[int]float64
// This describes the quantity of loci from both parents that are phased
// For example, if there are 10 loci for this trait, and one parent has 10 phased loci and the other has 5,
// this variable will have a value of 15
QuantityOfParentalPhasedLoci int
QuantityOfLociKnown int
// A list of 100 offspring outcomes for 100 prospective offspring from the genome pair
// Example: A list of heights for 100 prospective offspring
SampleOffspringOutcomesList []float64
}

View file

@ -11,6 +11,7 @@ package geneticPrediction
// We could create slower models that provide more accurate predictions
import "seekia/resources/geneticReferences/traits"
import "seekia/resources/geneticPredictionModels"
import "seekia/internal/genetics/locusValue"
import "seekia/internal/genetics/readBiobankData"
@ -211,6 +212,252 @@ func DecodeBytesToNeuralNetworkObject(inputNeuralNetwork []byte)(NeuralNetwork,
return newNeuralNetworkObject, nil
}
// This map is used to store information about how accurate genetic prediction models are
// Map Structure: Trait Outcome Info -> Trait Prediction Accuracy Info
type TraitPredictionAccuracyInfoMap map[TraitOutcomeInfo]TraitPredictionAccuracyInfo
type TraitOutcomeInfo struct{
// This is the outcome which was found
// Example: "Blue"
OutcomeName string
// This is a value between 0-100 which describes the percentage of the loci which were tested for the input for the prediction
PercentageOfLociTested int
// This is a value between 0-100 which describes the percentage of the tested loci which were phased for the input for the prediction
PercentageOfPhasedLoci int
}
type TraitPredictionAccuracyInfo struct{
// This contains the quantity of examples for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
QuantityOfExamples int
// This contains the quantity of predictions for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
// Prediction = our model predicted this outcome
QuantityOfPredictions int
// This stores the probability (0-100) that our model will accurately predict this outcome for a genome which has
// the specified percentageOfLociTested and percentageOfPhasedLoci
// In other words: What is the probability that if you give Seekia a blue-eyed genome, it will give you a correct Blue prediction?
// This value is only accurate is QuantityOfExamples > 0
ProbabilityOfCorrectGenomePrediction int
// This stores the probability (0-100) that our model is correct if our model predicts that a genome
// with the specified percentageOfLociTested and percentageOfPhasedLoci has this outcome
// In other words: What is the probability that if Seekia says a genome will have blue eyes, it is correct?
// This value is only accurate is QuantityOfPredictions > 0
ProbabilityOfCorrectOutcomePrediction int
}
func EncodeTraitPredictionAccuracyInfoMapToBytes(inputMap TraitPredictionAccuracyInfoMap)([]byte, error){
buffer := new(bytes.Buffer)
encoder := gob.NewEncoder(buffer)
err := encoder.Encode(inputMap)
if (err != nil) { return nil, err }
inputMapBytes := buffer.Bytes()
return inputMapBytes, nil
}
func DecodeBytesToTraitPredictionAccuracyInfoMap(inputBytes []byte)(TraitPredictionAccuracyInfoMap, error){
if (inputBytes == nil){
return nil, errors.New("DecodeBytesToTraitPredictionAccuracyInfoMap called with nil inputBytes.")
}
buffer := bytes.NewBuffer(inputBytes)
decoder := gob.NewDecoder(buffer)
var newTraitPredictionAccuracyInfoMap TraitPredictionAccuracyInfoMap
err := decoder.Decode(&newTraitPredictionAccuracyInfoMap)
if (err != nil){ return nil, err }
return newTraitPredictionAccuracyInfoMap, nil
}
//Outputs:
// -bool: Neural network model exists for this trait (trait prediction is possible for this trait)
// -bool: Trait prediction is possible for this user (User has at least 1 known trait locus value)
// -string: Predicted trait outcome (Example: "Blue")
// -int: Confidence: Probability (0-100) that the prediction is accurate
// -int: Quantity of loci known
// -int: Quantity of phased loci
// -error
func GetNeuralNetworkTraitPredictionFromGenomeMap(traitName string, genomeMap map[int64]locusValue.LocusValue)(bool, bool, string, int, int, int, error){
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return false, false, "", 0, 0, 0, err }
// This is a map of rsIDs which influence this trait
traitRSIDsList := traitObject.LociList
if (len(traitRSIDsList) == 0){
// Neural network trait prediction is not possible for this trait
return false, false, "", 0, 0, 0, nil
}
predictionModelExists, predictionModelBytes := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
if (predictionModelExists == false){
// Neural network trait prediction is not possible for this trait
return false, false, "", 0, 0, 0, nil
}
traitRSIDsListCopy := slices.Clone(traitRSIDsList)
slices.Sort(traitRSIDsListCopy)
// In the inputLayer, each locus value is represented by 3 neurons:
// 1. LocusExists/LocusIsPhased
// -0 = Locus value is unknown
// -0.5 = Locus Is known, phase is unknown
// -1 = Locus Is Known, phase is known
// 2. Allele1 Locus Value (Value between 0-1)
// -0 = Value is unknown
// 3. Allele2 Locus Value (Value between 0-1)
// -0 = Value is unknown
//
neuralNetworkInput := make([]float32, 0)
quantityOfLociKnown := 0
quantityOfPhasedLoci := 0
for _, rsID := range traitRSIDsListCopy{
userLocusValue, exists := genomeMap[rsID]
if (exists == false){
neuralNetworkInput = append(neuralNetworkInput, 0, 0, 0)
continue
}
quantityOfLociKnown += 1
locusAllele1 := userLocusValue.Base1Value
locusAllele2 := userLocusValue.Base2Value
locusIsPhased := userLocusValue.LocusIsPhased
getNeuron1 := func()float32{
if (locusIsPhased == false){
return 0.5
}
quantityOfPhasedLoci += 1
return 1
}
neuron1 := getNeuron1()
neuron2, err := convertAlleleToNeuron(locusAllele1)
if (err != nil) { return false, false, "", 0, 0, 0, err }
neuron3, err := convertAlleleToNeuron(locusAllele2)
if (err != nil) { return false, false, "", 0, 0, 0, err }
neuralNetworkInput = append(neuralNetworkInput, neuron1, neuron2, neuron3)
}
if (quantityOfLociKnown == 0){
// We can't predict anything about this trait for this genome
return true, false, "", 0, 0, 0, nil
}
neuralNetworkObject, err := DecodeBytesToNeuralNetworkObject(predictionModelBytes)
if (err != nil) { return false, false, "", 0, 0, 0, err }
outputLayer, err := GetNeuralNetworkRawPrediction(&neuralNetworkObject, neuralNetworkInput)
if (err != nil) { return false, false, "", 0, 0, 0, err }
predictedOutcomeName, err := GetOutcomeNameFromOutputLayer(traitName, false, outputLayer)
if (err != nil) { return false, false, "", 0, 0, 0, err }
modelTraitAccuracyInfoFile, err := geneticPredictionModels.GetPredictionModelTraitAccuracyInfoBytes(traitName)
if (err != nil) { return false, false, "", 0, 0, 0, err }
modelTraitAccuracyInfoMap, err := DecodeBytesToTraitPredictionAccuracyInfoMap(modelTraitAccuracyInfoFile)
if (err != nil) { return false, false, "", 0, 0, 0, err }
// We find the model trait accuracy info object that is the most similar to our predicted outcome
getPredictionAccuracy := func()int{
totalNumberOfTraitLoci := len(traitRSIDsList)
proportionOfLociTested := float64(quantityOfLociKnown)/float64(totalNumberOfTraitLoci)
percentageOfLociTested := int(proportionOfLociTested * 100)
proportionOfPhasedLoci := float64(quantityOfPhasedLoci)/float64(totalNumberOfTraitLoci)
percentageOfPhasedLoci := int(proportionOfPhasedLoci * 100)
// This is a value between 0 and 100 that represents the most likely accuracy probability for this prediction
closestPredictionAccuracy := 0
// This is a value that represents the distance our closest prediction accuracy has from the current prediction
// Consider each prediction accuracy value on an (X,Y) coordinate plane
// X = Number of loci tested
// Y = Number of phased loci
closestPredictionAccuracyDistance := float64(0)
anyOutcomeAccuracyFound := false
for traitOutcomeInfo, traitPredictionAccuracyInfo := range modelTraitAccuracyInfoMap{
outcomeName := traitOutcomeInfo.OutcomeName
if (outcomeName != predictedOutcomeName){
continue
}
probabilityOfCorrectOutcomePrediction := traitPredictionAccuracyInfo.ProbabilityOfCorrectOutcomePrediction
currentPercentageOfLociTested := traitOutcomeInfo.PercentageOfLociTested
currentPercentageOfPhasedLoci := traitOutcomeInfo.PercentageOfPhasedLoci
// Distance Formula for 2 coordinates (x1, y1) and (x2, y2):
// distance = √((x2 - x1)^2 + (y2 - y1)^2)
differenceInX := float64(currentPercentageOfLociTested - percentageOfLociTested)
differenceInY := float64(currentPercentageOfPhasedLoci - percentageOfPhasedLoci)
distance := math.Sqrt(math.Pow(differenceInX, 2) + math.Pow(differenceInY, 2))
if (distance == 0){
// We found the exact prediction accuracy
return probabilityOfCorrectOutcomePrediction
}
if (anyOutcomeAccuracyFound == false){
closestPredictionAccuracyDistance = distance
closestPredictionAccuracy = probabilityOfCorrectOutcomePrediction
anyOutcomeAccuracyFound = true
continue
} else {
if (distance < closestPredictionAccuracyDistance){
closestPredictionAccuracyDistance = distance
closestPredictionAccuracy = probabilityOfCorrectOutcomePrediction
}
}
}
if (anyOutcomeAccuracyFound == false){
// This means that our model has never actually predicted this outcome
// This shouldn't happen unless our model is really bad, or our training set has very few people with this outcome.
// We return a 0% accuracy rating
return 0
}
return closestPredictionAccuracy
}
predictionAccuracy := getPredictionAccuracy()
return true, true, predictedOutcomeName, predictionAccuracy, quantityOfLociKnown, quantityOfPhasedLoci, nil
}
//Outputs:
// -int: Number of loci values that are known
// -int: Number of loci values that are known and phased
@ -437,9 +684,9 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
if (err != nil) { return false, nil, err }
// This is a list of rsIDs which influence this trait
traitRSIDs := traitObject.LociList
traitRSIDsList := traitObject.LociList
if (len(traitRSIDs) == 0){
if (len(traitRSIDsList) == 0){
return false, nil, errors.New("traitObject contains no rsIDs.")
}
@ -457,7 +704,7 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
// -0 = Locus value is unknown
// -0.5 = Locus Is known, phase is unknown
// -1 = Locus Is Known, phase is known
expectedNumberOfInputLayerRows := len(traitRSIDs) * 3
expectedNumberOfInputLayerRows := len(traitRSIDsList) * 3
if (numberOfInputLayerRows != expectedNumberOfInputLayerRows){
@ -468,7 +715,7 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
checkIfAnyTraitLocusValuesExist := func()bool{
for _, rsID := range traitRSIDs{
for _, rsID := range traitRSIDsList{
_, exists := userLocusValuesMap[rsID]
if (exists == true){
@ -487,11 +734,9 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
}
// We sort rsIDs in ascending order
// We copy list so we don't change the original
traitRSIDsList := slices.Clone(traitRSIDs)
slices.Sort(traitRSIDsList)
traitRSIDsListCopy := slices.Clone(traitRSIDsList)
slices.Sort(traitRSIDsListCopy)
// This function returns the outputLayer for all trainingDatas for this user
// Each outputLayer represents the user's trait value (Example: "Blue" for Eye Color)
@ -637,11 +882,11 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
anyLocusExists := false
inputLayerLength := len(traitRSIDsList) * 3
inputLayerLength := len(traitRSIDsListCopy) * 3
inputLayer := make([]float32, 0, inputLayerLength)
for _, rsID := range traitRSIDsList{
for _, rsID := range traitRSIDsListCopy{
randomFloat := pseudorandomNumberGenerator.Float64()
if (randomFloat > probabilityOfUsingLoci){
@ -996,7 +1241,7 @@ func (inputNetwork *NeuralNetwork)buildNeuralNetwork(inputLayer *gorgonia.Node)e
inputLayerCopy := inputLayer
// We multiply weights at each layer and perform sigmoid after each multiplication
// We multiply weights at each layer and perform ReLU (Rectification) after each multiplication
weights1 := inputNetwork.weights1

View file

@ -27,7 +27,6 @@ var myCacheChosenGeneticAnalysisIdentifier string
// We use this variable to store the analysis in memory
// This prevents us from having to read and unmarshal the messagepack file each time we want to retrieve the analysis
// TODO: Read attributes from the analysis into maps for faster retrieval
var myCacheChosenGeneticAnalysis geneticAnalysis.PersonAnalysis
// These variables store metadata about the cache genetic analysis
@ -36,6 +35,7 @@ var myCacheChosenGeneticAnalysis_GenomeIdentifierToUse [16]byte
// This variable tells us if the current cache chosen genetic analysis contains multiple genomes
var myCacheChosenGeneticAnalysis_MultipleGenomesExist bool
// This function is used to retrieve the user's chosen person genetic analysis
// The user must choose a person to link to their mate profile/identity
// This genetic analysis is not shared on the person's profile publicly.
@ -101,7 +101,7 @@ func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, geneticAnalysis.PersonAn
return false, false, false, emptyPersonAnalysis, [16]byte{}, false, errors.New("CheckIfPersonAnalysisIsReady returning missing genetic analysis.")
}
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err }
getGenomeIdentifierToUse := func()([16]byte, error){

View file

@ -6,6 +6,7 @@ package myGenomes
import "seekia/internal/cryptography/blake3"
import "seekia/internal/encoding"
import "seekia/internal/genetics/prepareRawGenomes"
import "seekia/internal/genetics/readRawGenomes"
import "seekia/internal/helpers"
import "seekia/internal/localFilesystem"
@ -106,11 +107,18 @@ func AddRawGenome(personIdentifier string, rawGenomeString string)(bool, bool, e
rawGenomeReader := strings.NewReader(rawGenomeString)
companyName, importVersion, timeFileWasGenerated, snpCount, genomeIsPhased, _, err := readRawGenomes.ReadRawGenomeFile(rawGenomeReader)
companyName, importVersion, timeFileWasGenerated, snpCount, genomeIsPhased, rawGenomeMap, err := readRawGenomes.ReadRawGenomeFile(rawGenomeReader)
if (err != nil){
return false, false, nil
}
genomeHasUsefulLocations, _, err := prepareRawGenomes.ConvertRawGenomeToGenomeMap(rawGenomeMap, genomeIsPhased)
if (err != nil) { return false, false, err }
if (genomeHasUsefulLocations == false){
//TODO: Explain this to the user rather than just telling the user that the file is invalid
return false, false, nil
}
genomeIdentifier, err := helpers.GetNewRandomHexString(16)
if (err != nil) { return false, false, err }

View file

@ -76,6 +76,10 @@ func CreateRawGenomeWithMetadataObject(genomeIdentifier [16]byte, rawGenomeStrin
// -error
func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error)([]GenomeWithMetadata, [][16]byte, bool, [16]byte, [16]byte, error){
if (len(inputGenomesList) == 0){
return nil, nil, false, [16]byte{}, [16]byte{}, errors.New("GetGenomesWithMetadataListFromRawGenomesList called with empty inputGenomesList")
}
// The reading of genomes will take up the first 20% of the percentage range
// The creation of multiple genomes will take up the last 80% of the percentage range
@ -102,103 +106,13 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
// Now we convert rawGenomeMap to a genomeMap
// Map Structure: RSID -> Locus Value
genomeMap := make(map[int64]locusValue.LocusValue)
// We use this list to check for alias collisions later
allRSIDsList := make([]int64, 0)
for rsID, locusBasePairValue := range rawGenomeMap{
locusAllele2Exists := locusBasePairValue.Allele2Exists
if (locusAllele2Exists == false){
// This SNP contains less than 2 bases
// We don't support reading these kinds of SNP values yet
continue
}
locusAllele1 := locusBasePairValue.Allele1
locusAllele2 := locusBasePairValue.Allele2
getLocusIsPhasedBool := func()bool{
if (locusAllele1 == locusAllele2){
// Locus has to be phased, because phase flip does not change value
return true
}
return genomeIsPhased
}
locusIsPhased := getLocusIsPhasedBool()
locusValueObject := locusValue.LocusValue{
LocusIsPhased: locusIsPhased,
Base1Value: locusAllele1,
Base2Value: locusAllele2,
}
genomeMap[rsID] = locusValueObject
allRSIDsList = append(allRSIDsList, rsID)
}
// Now we check for rsID aliases
// rsID aliases are multiple rsids that refer to the same location
// If a single genome file contained two identical rsids whose value conflicted, the company must have made an error in reporting
for _, rsID := range allRSIDsList{
rsidLocusValue, exists := genomeMap[rsID]
if (exists == false){
// This must have been an alias that was deleted
continue
}
aliasExists, rsidAliasesList, err := locusMetadata.GetRSIDAliases(rsID)
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
if (aliasExists == false){
continue
}
checkIfRSIDCollisionExists := func()bool{
for _, rsidAlias := range rsidAliasesList{
aliasLocusValue, exists := genomeMap[rsidAlias]
if (exists == false){
continue
}
if (aliasLocusValue != rsidLocusValue){
// A collision exists with an alias rsID
// The company must be creating invalid results, or our alias list is invalid
return true
}
}
return false
}
rsidCollisionExists := checkIfRSIDCollisionExists()
if (rsidCollisionExists == true){
// We will delete this rsID
// We cannot trust any of the results
delete(genomeMap, rsID)
}
// We delete all aliases from the genome map
// We do this to save space
for _, rsidAlias := range rsidAliasesList{
delete(genomeMap, rsidAlias)
}
}
if (len(genomeMap) == 0){
// No valid locations exist
continue
anyValuesExist, genomeMap, err := ConvertRawGenomeToGenomeMap(rawGenomeMap, genomeIsPhased)
if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err }
if (anyValuesExist == false){
// We have to make sure this never happens so the user isn't confused as to why genomes
// that were imported were not included in the analysis
// We make sure this doesn't happen by verifying the genome at the time of importing
return nil, nil, false, [16]byte{}, [16]byte{}, errors.New("Genome supplied to GetGenomesWithMetadataListFromRawGenomesList has no valid locations.")
}
genomeWithMetadataObject := GenomeWithMetadata{
@ -221,9 +135,9 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
err := updatePercentageCompleteFunction(20)
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
if (len(genomesWithMetadataList) == 1){
if (len(genomesWithMetadataList) <= 1){
// Only 1 genome exists.
// <=1 genome exists.
// No genome combining is needed.
err = updatePercentageCompleteFunction(100)
@ -547,4 +461,112 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
return genomesWithMetadataList, allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil
}
//Outputs:
// -bool: Genome has any useful locations
// -map[int64]locusValue.LocusValue
// -error
func ConvertRawGenomeToGenomeMap(rawGenomeMap map[int64]readRawGenomes.RawGenomeLocusValue, genomeIsPhased bool)(bool, map[int64]locusValue.LocusValue, error){
// Map Structure: RSID -> Locus Value
genomeMap := make(map[int64]locusValue.LocusValue)
// We use this list to check for alias collisions later
allRSIDsList := make([]int64, 0)
for rsID, locusBasePairValue := range rawGenomeMap{
locusAllele2Exists := locusBasePairValue.Allele2Exists
if (locusAllele2Exists == false){
// This SNP contains less than 2 bases
// We don't support reading these kinds of SNP values yet
continue
}
locusAllele1 := locusBasePairValue.Allele1
locusAllele2 := locusBasePairValue.Allele2
getLocusIsPhasedBool := func()bool{
if (locusAllele1 == locusAllele2){
// Locus has to be phased, because phase flip does not change value
return true
}
return genomeIsPhased
}
locusIsPhased := getLocusIsPhasedBool()
locusValueObject := locusValue.LocusValue{
Base1Value: locusAllele1,
Base2Value: locusAllele2,
LocusIsPhased: locusIsPhased,
}
genomeMap[rsID] = locusValueObject
allRSIDsList = append(allRSIDsList, rsID)
}
// Now we check for rsID aliases
// rsID aliases are multiple rsids that refer to the same location
// If a single genome file contained two identical rsids whose value conflicted, the company must have made an error in reporting
for _, rsID := range allRSIDsList{
rsidLocusValue, exists := genomeMap[rsID]
if (exists == false){
// This must have been an alias that was deleted
continue
}
aliasExists, rsidAliasesList, err := locusMetadata.GetRSIDAliases(rsID)
if (err != nil){ return false, nil, err }
if (aliasExists == false){
continue
}
checkIfRSIDCollisionExists := func()bool{
for _, rsidAlias := range rsidAliasesList{
aliasLocusValue, exists := genomeMap[rsidAlias]
if (exists == false){
continue
}
if (aliasLocusValue != rsidLocusValue){
// A collision exists with an alias rsID
// The company must be creating invalid results, or our alias list is invalid
return true
}
}
return false
}
rsidCollisionExists := checkIfRSIDCollisionExists()
if (rsidCollisionExists == true){
// We will delete this rsID
// We cannot trust any of the results
delete(genomeMap, rsID)
}
// We delete all aliases from the genome map
// We do this to save space
for _, rsidAlias := range rsidAliasesList{
delete(genomeMap, rsidAlias)
}
}
if (len(genomeMap) == 0){
// No valid locations exist
return false, nil, nil
}
return true, genomeMap, nil
}

View file

@ -47,27 +47,30 @@ func ReadCoupleGeneticAnalysisString(inputAnalysisString string)(geneticAnalysis
// -bool: Multiple genomes exist
// -[16]byte: OnlyExcludeConflicts GenomeIdentifier
// -[16]byte: OnlyIncludeShared GenomeIdentifier
// -map[[16]byte]map[int64]locusValue.LocusValue: Genomes locus values map
// -error
func GetMetadataFromPersonGeneticAnalysis(inputGeneticAnalysis geneticAnalysis.PersonAnalysis)([][16]byte, bool, [16]byte, [16]byte, error){
func GetMetadataFromPersonGeneticAnalysis(inputGeneticAnalysis geneticAnalysis.PersonAnalysis)([][16]byte, bool, [16]byte, [16]byte, map[[16]byte]map[int64]locusValue.LocusValue, error){
analysisVersion := inputGeneticAnalysis.AnalysisVersion
if (analysisVersion != 1){
// This analysis must have been created by a newer version of Seekia
// We cannot read it
return nil, false, [16]byte{}, [16]byte{}, errors.New("Cannot read analysis: Is a newer analysis version.")
return nil, false, [16]byte{}, [16]byte{}, nil, errors.New("Cannot read analysis: Is a newer analysis version.")
}
allRawGenomeIdentifiersList := inputGeneticAnalysis.AllRawGenomeIdentifiersList
genomesMap := inputGeneticAnalysis.GenomesMap
combinedGenomesExist := inputGeneticAnalysis.CombinedGenomesExist
if (combinedGenomesExist == false){
return allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, nil
return allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, genomesMap, nil
}
onlyExcludeConflictsGenomeIdentifier := inputGeneticAnalysis.OnlyExcludeConflictsGenomeIdentifier
onlyIncludeSharedGenomeIdentifier := inputGeneticAnalysis.OnlyIncludeSharedGenomeIdentifier
return allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil
return allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, genomesMap, nil
}
//Outputs:
@ -178,7 +181,7 @@ func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1 bool,
return inputGenomeIdentifier, true, false, "", nil
}
_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(person1AnalysisObject)
_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(person1AnalysisObject)
if (err != nil) { return [16]byte{}, false, false, "", err }
if (multipleGenomesExist == false){
return [16]byte{}, false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.")
@ -204,7 +207,7 @@ func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1 bool,
return inputGenomeIdentifier, true, false, "", nil
}
_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(person2AnalysisObject)
_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(person2AnalysisObject)
if (err != nil) { return [16]byte{}, false, false, "", err }
if (multipleGenomesExist == false){
return [16]byte{}, false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.")
@ -226,9 +229,9 @@ func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1 bool,
// -bool: Person has disease
// -int: Probability of passing a disease variant
// -string: Probability of passing a disease variant formatted (with % suffix)
// -int: Number of variants tested
// -int: Number of loci tested
// -int: Number of phased loci
// -int: Quantity of variants tested
// -int: Quantity of loci tested
// -int: Quantity of phased loci
// -bool: Conflict exists
// -error
func GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, bool, int, string, int, int, int, bool, error){
@ -250,16 +253,16 @@ func GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject genet
conflictExists := personMonogenicDiseaseInfo.ConflictExists
personHasDisease := genomeMonogenicDiseaseInfo.PersonHasDisease
numberOfVariantsTested := genomeMonogenicDiseaseInfo.NumberOfVariantsTested
numberOfLociTested := genomeMonogenicDiseaseInfo.NumberOfLociTested
numberOfPhasedLoci := genomeMonogenicDiseaseInfo.NumberOfPhasedLoci
quantityOfVariantsTested := genomeMonogenicDiseaseInfo.QuantityOfVariantsTested
quantityOfLociTested := genomeMonogenicDiseaseInfo.QuantityOfLociTested
quantityOfPhasedLoci := genomeMonogenicDiseaseInfo.QuantityOfPhasedLoci
probabilityOfPassingAVariant := genomeMonogenicDiseaseInfo.ProbabilityOfPassingADiseaseVariant
probabilityOfPassingAVariantString := helpers.ConvertIntToString(probabilityOfPassingAVariant)
probabilityOfPassingAVariantFormatted := probabilityOfPassingAVariantString + "%"
return true, personHasDisease, probabilityOfPassingAVariant, probabilityOfPassingAVariantFormatted, numberOfVariantsTested, numberOfLociTested, numberOfPhasedLoci, conflictExists, nil
return true, personHasDisease, probabilityOfPassingAVariant, probabilityOfPassingAVariantFormatted, quantityOfVariantsTested, quantityOfLociTested, quantityOfPhasedLoci, conflictExists, nil
}
@ -485,24 +488,23 @@ func GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisOb
// -bool: Polygenic Disease Risk Score known (any loci values exist)
// -int: Person Disease risk score
// -string: Person Disease risk score formatted (has "/10" suffix)
// -map[int]locusValue.LocusValue: Person locus values map
// -int: Number of loci tested
// -int: Quantity of loci tested
// -bool: Conflict exists
// -error
func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, int, string, map[int64]locusValue.LocusValue, int, bool, error){
func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, int, string, int, bool, error){
personPolygenicDiseasesMap := personAnalysisObject.PolygenicDiseasesMap
personPolygenicDiseaseInfo, exists := personPolygenicDiseasesMap[diseaseName]
if (exists == false){
return false, 0, "", nil, 0, false, nil
return false, 0, "", 0, false, nil
}
personPolygenicDiseaseInfoMap := personPolygenicDiseaseInfo.PolygenicDiseaseInfoMap
genomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier]
if (exists == false){
return false, 0, "", nil, 0, false, nil
return false, 0, "", 0, false, nil
}
conflictExists := personPolygenicDiseaseInfo.ConflictExists
@ -513,11 +515,9 @@ func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject genet
personDiseaseRiskScoreFormatted := personDiseaseRiskScoreString + "/10"
personLocusValuesMap := genomePolygenicDiseaseInfo.LocusValuesMap
quantityOfLociTested := genomePolygenicDiseaseInfo.QuantityOfLociTested
numberOfLociTested := genomePolygenicDiseaseInfo.NumberOfLociTested
return true, personDiseaseRiskScore, personDiseaseRiskScoreFormatted, personLocusValuesMap, numberOfLociTested, conflictExists, nil
return true, personDiseaseRiskScore, personDiseaseRiskScoreFormatted, quantityOfLociTested, conflictExists, nil
}
@ -526,7 +526,7 @@ func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject genet
// -int: Offspring average disease risk score
// -string: Offspring Disease average risk score formatted (has "/10" suffix)
// -[]int: Sample Offspring Risk Scores List
// -int: Number of loci tested
// -int: Quantity of loci tested
// -bool: Conflict exists
// -error
func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, string, []int, int, bool, error){
@ -547,7 +547,7 @@ func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject ge
conflictExists := couplePolygenicDiseaseInfo.ConflictExists
numberOfLociTested := genomePairPolygenicDiseaseInfo.NumberOfLociTested
quantityOfLociTested := genomePairPolygenicDiseaseInfo.QuantityOfLociTested
offspringAverageRiskScore := genomePairPolygenicDiseaseInfo.OffspringAverageRiskScore
@ -557,7 +557,7 @@ func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject ge
sampleOffspringRiskScoresList := genomePairPolygenicDiseaseInfo.SampleOffspringRiskScoresList
return true, offspringAverageRiskScore, offspringAverageRiskScoreFormatted, sampleOffspringRiskScoresList, numberOfLociTested, conflictExists, nil
return true, offspringAverageRiskScore, offspringAverageRiskScoreFormatted, sampleOffspringRiskScoresList, quantityOfLociTested, conflictExists, nil
}
//Outputs:
@ -673,71 +673,169 @@ func GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObje
}
//Outputs:
// -map[int64]locusValue.LocusValue (rsID -> Base pair) (missing rsIDs represent unknown values)
// -bool: Neural network exists
// -bool: Any neural network analysis exists
// -string: Neural network predicted outcome
// -int: Prediction confidence
// -int: Quantity of loci known (Neural network)
// -int: Quantity of phased loci (Neural network)
// -bool: Any trait rules exist (Rule-based analysis is possible)
// -bool: Any Trait Rule known/tested
// -map[string]int: Trait outcomes scores map (Outcome -> Number of points)
// -int: Number of rules tested
// -bool: Conflict exists
// -map[[3]byte]bool: Rule Identifier -> Person passes rule
// -bool: Predicted outcome exists
// -string: Predicted outcome
// -int: Quantity of rules tested
// -int: Quantity Of Loci Known (Rules)
// -bool: Conflict exists (between any of these results for each genome)
// -error
func GetPersonTraitInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte)(map[int64]locusValue.LocusValue, bool, map[string]int, int, bool, error){
func GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte)(bool, bool, string, int, int, int, bool, bool, map[[3]byte]bool, bool, string, int, int, bool, error){
personTraitsMap := personAnalysisObject.TraitsMap
personTraitsMap := personAnalysisObject.DiscreteTraitsMap
personTraitInfoObject, exists := personTraitsMap[traitName]
if (exists == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, false, nil, 0, false, nil
return false, false, "", 0, 0, 0, false, false, nil, false, "", 0, 0, false, errors.New("Person trait analysis is missing trait: " + traitName)
}
personTraitInfoMap := personTraitInfoObject.TraitInfoMap
conflictExists := personTraitInfoObject.ConflictExists
personGenomeTraitInfoObject, exists := personTraitInfoMap[genomeIdentifier]
if (exists == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, false, nil, 0, false, nil
return false, false, "", 0, 0, 0, false, false, nil, false, "", 0, 0, false, errors.New("personTraitInfoMap in Person analysis is missing map for genome identifier.")
}
conflictExists := personTraitInfoObject.ConflictExists
neuralNetworkExists := personGenomeTraitInfoObject.NeuralNetworkExists
neuralNetworkAnalysisExists := personGenomeTraitInfoObject.NeuralNetworkAnalysisExists
genomeNumberOfRulesTested := personGenomeTraitInfoObject.NumberOfRulesTested
getNeuralNetworkAnalysisInfo := func()(string, int, int, int){
genomeLocusValuesMap := personGenomeTraitInfoObject.LocusValuesMap
if (neuralNetworkExists == false || neuralNetworkAnalysisExists == false){
return "", 0, 0, 0
}
genomeOutcomeScoresMap := personGenomeTraitInfoObject.OutcomeScoresMap
neuralNetworkAnalysis := personGenomeTraitInfoObject.NeuralNetworkAnalysis
return genomeLocusValuesMap, true, genomeOutcomeScoresMap, genomeNumberOfRulesTested, conflictExists, nil
predictedOutcome := neuralNetworkAnalysis.PredictedOutcome
predictionConfidence := neuralNetworkAnalysis.PredictionConfidence
quantityOfLociKnown := neuralNetworkAnalysis.QuantityOfLociKnown
quantityOfPhasedLoci := neuralNetworkAnalysis.QuantityOfPhasedLoci
return predictedOutcome, predictionConfidence, quantityOfLociKnown, quantityOfPhasedLoci
}
neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, quantityOfLociKnown_NeuralNetwork, quantityOfPhasedLoci_NeuralNetwork :=
getNeuralNetworkAnalysisInfo()
anyRulesExist := personGenomeTraitInfoObject.AnyRulesExist
rulesAnalysisExists := personGenomeTraitInfoObject.RulesAnalysisExists
getTraitAnalysisInfo := func()(map[[3]byte]bool, bool, string, int, int){
if (anyRulesExist == false || rulesAnalysisExists == false){
return nil, false, "", 0, 0
}
rulesAnalysisObject := personGenomeTraitInfoObject.RulesAnalysis
genomePassesRulesMap := rulesAnalysisObject.GenomePassesRulesMap
predictedOutcomeExists := rulesAnalysisObject.PredictedOutcomeExists
predictedOutcome := rulesAnalysisObject.PredictedOutcome
quantityOfRulesTested := rulesAnalysisObject.QuantityOfRulesTested
quantityOfLociKnown := rulesAnalysisObject.QuantityOfLociKnown
return genomePassesRulesMap, predictedOutcomeExists, predictedOutcome, quantityOfRulesTested, quantityOfLociKnown
}
genomePassesRulesMap, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, quantityOfLociKnown_Rules := getTraitAnalysisInfo()
return neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, quantityOfLociKnown_NeuralNetwork, quantityOfPhasedLoci_NeuralNetwork, anyRulesExist, rulesAnalysisExists, genomePassesRulesMap, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, quantityOfLociKnown_Rules, conflictExists, nil
}
//Outputs:
// -bool: Trait Outcome Scores known
// -map[string]float64: Trait average outcome scores map (OutcomeName -> AverageScore)
// -int: Number of rules tested
// -bool: Conflict exists
// -bool: Neural network exists
// -bool: Neural network analysis exists
// -map[string]int: Offspring outcome probabilities map for neural network prediction
// -Map Structure: Outcome name -> Probability of outcome (0-100)
// -int: Average confidence (for neural network prediction)
// -int: Quantity of loci known (for neural network)
// -int: Quantity of Parental phased loci
// -bool: Any Rules exist
// -bool: Rules analysis exists
// -map[string]int: Offspring outcome probabilities map for rules-based prediction
// -Map Structure: Outcome name -> Probability of outcome (0-100)
// -map[[3]byte]int: Offspring probability of passing rules map
// -Map Structure: Rule Identifier -> Probability of passing rule (0-100)
// -int: Quantity of rules tested
// -int: Quantity of loci known (For Rules)
// -bool: Conflict exists (Between this genome pair and other genome pairs)
// -error
func GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte)(bool, map[string]float64, int, bool, error){
func GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte)(bool, bool, map[string]int, int, int, int, bool, bool, map[string]int, map[[3]byte]int, int, int, bool, error){
offspringTraitsMap := coupleAnalysisObject.TraitsMap
offspringTraitsMap := coupleAnalysisObject.DiscreteTraitsMap
traitInfoObject, exists := offspringTraitsMap[traitName]
if (exists == false){
return false, nil, 0, false, nil
return false, false, nil, 0, 0, 0, false, false, nil, nil, 0, 0, false, errors.New("offspringTraitsMap missing trait when reading couple genetic analysis: " + traitName)
}
traitInfoMap := traitInfoObject.TraitInfoMap
conflictExists := traitInfoObject.ConflictExists
genomePairTraitInfoObject, exists := traitInfoMap[genomePairIdentifier]
if (exists == false){
return false, nil, 0, false, nil
return false, false, nil, 0, 0, 0, false, false, nil, nil, 0, 0, false, errors.New("traitInfoMap missing trait info for genome pair when reading from genetic analysis.")
}
conflictExists := traitInfoObject.ConflictExists
neuralNetworkExists := genomePairTraitInfoObject.NeuralNetworkExists
numberOfRulesTested := genomePairTraitInfoObject.NumberOfRulesTested
offspringAverageOutcomeScoresMap := genomePairTraitInfoObject.OffspringAverageOutcomeScoresMap
neuralNetworkAnalysisExists := genomePairTraitInfoObject.NeuralNetworkAnalysisExists
return true, offspringAverageOutcomeScoresMap, numberOfRulesTested, conflictExists, nil
getGenomePairTraitNeuralNetworkAnalysisInfo := func()(map[string]int, int, int, int){
if (neuralNetworkExists == false || neuralNetworkAnalysisExists == false){
return nil, 0, 0, 0
}
genomePairTraitNeuralNetworkInfo := genomePairTraitInfoObject.NeuralNetworkAnalysis
offspringOutcomeProbabilitiesMap := genomePairTraitNeuralNetworkInfo.OffspringOutcomeProbabilitiesMap
averageConfidence := genomePairTraitNeuralNetworkInfo.AverageConfidence
quantityOfLociKnown := genomePairTraitNeuralNetworkInfo.QuantityOfLociKnown
quantityOfParentalPhasedLoci := genomePairTraitNeuralNetworkInfo.QuantityOfParentalPhasedLoci
return offspringOutcomeProbabilitiesMap, averageConfidence, quantityOfLociKnown, quantityOfParentalPhasedLoci
}
offspringOutcomeProbabilitiesMap_NeuralNetwork, averageConfidence_NeuralNetwork, quantityOfLociKnown_NeuralNetwork, quantityOfParentalPhasedLoci_NeuralNetwork := getGenomePairTraitNeuralNetworkAnalysisInfo()
anyRulesExist := genomePairTraitInfoObject.RulesExist
rulesAnalysisExists := genomePairTraitInfoObject.RulesAnalysisExists
getGenomePairTraitRulesAnalysisInfo := func()(map[string]int, map[[3]byte]int, int, int){
if (anyRulesExist == false || rulesAnalysisExists == false){
return nil, nil, 0, 0
}
genomePairTraitInfo_Rules := genomePairTraitInfoObject.RulesAnalysis
offspringOutcomeProbabilitiesMap := genomePairTraitInfo_Rules.OffspringOutcomeProbabilitiesMap
probabilityOfPassingRulesMap := genomePairTraitInfo_Rules.ProbabilityOfPassingRulesMap
quantityOfRulesTested := genomePairTraitInfo_Rules.QuantityOfRulesTested
quantityOfLociKnown := genomePairTraitInfo_Rules.QuantityOfLociKnown
return offspringOutcomeProbabilitiesMap, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown
}
offspringOutcomeProbabilitiesMap_Rules, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown_Rules := getGenomePairTraitRulesAnalysisInfo()
return neuralNetworkExists, neuralNetworkAnalysisExists, offspringOutcomeProbabilitiesMap_NeuralNetwork, averageConfidence_NeuralNetwork, quantityOfLociKnown_NeuralNetwork, quantityOfParentalPhasedLoci_NeuralNetwork, anyRulesExist, rulesAnalysisExists, offspringOutcomeProbabilitiesMap_Rules, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown_Rules, conflictExists, nil
}
@ -745,24 +843,17 @@ func GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalys
// -bool: Rule status is known (we know if the rule is passed or not)
// -bool: Genome passes rule
// -error
func GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, genomeIdentifier [16]byte)(bool, bool, error){
func GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, genomeIdentifier [16]byte)(bool, bool, error){
personTraitsMap := personAnalysisObject.TraitsMap
traitInfoObject, exists := personTraitsMap[traitName]
if (exists == false){
_, _, _, _, _, _, anyRulesExist, rulesAnalysisExists, genomePassesRulesMap, _, _, _, _, _, err := GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
if (err != nil) { return false, false, err }
if (anyRulesExist == false){
return false, false, errors.New("GetPersonTraitRuleInfoFromGeneticAnalysis called when no trait rules exist.")
}
if (rulesAnalysisExists == false){
return false, false, nil
}
personTraitInfoMap := traitInfoObject.TraitInfoMap
genomeTraitInfoObject, exists := personTraitInfoMap[genomeIdentifier]
if (exists == false){
return false, false, nil
}
genomePassesRulesMap := genomeTraitInfoObject.GenomePassesRulesMap
genomePassesRule, statusIsKnown := genomePassesRulesMap[ruleIdentifier]
if (statusIsKnown == false){
return false, false, nil
@ -777,24 +868,17 @@ func GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalysisObject geneticAnaly
// -int: Offspring probability of passing rule (0 - 100)
// -string: Offspring probability of passing rule formatted (with % suffix)
// -error
func GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, string, error){
func GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, string, error){
offspringTraitsMap := coupleAnalysisObject.TraitsMap
offspringTraitInfo, exists := offspringTraitsMap[traitName]
if (exists == false){
_, _, _, _, _, _, anyRulesExist, rulesAnalysisExists, _, offspringProbabilityOfPassingRulesMap, _, _, _, err := GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
if (err != nil) { return false, 0, "", err }
if (anyRulesExist == false){
return false, 0, "", errors.New("GetOffspringTraitRuleInfoFromGeneticAnalysis called for trait which has no rules: " + traitName)
}
if (rulesAnalysisExists == false){
return false, 0, "", nil
}
offspringTraitInfoMap := offspringTraitInfo.TraitInfoMap
offspringTraitInfoObject, exists := offspringTraitInfoMap[genomePairIdentifier]
if (exists == false){
return false, 0, "", nil
}
offspringProbabilityOfPassingRulesMap := offspringTraitInfoObject.ProbabilityOfPassingRulesMap
offspringProbabilityOfPassingRule, exists := offspringProbabilityOfPassingRulesMap[ruleIdentifier]
if (exists == false){
return false, 0, "", nil
@ -812,7 +896,7 @@ func GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject geneticAn
//TODO: Perform sanity checks on data
func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis)error{
allRawGenomeIdentifiersList, personHasMultipleGenomes, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(personAnalysisObject)
allRawGenomeIdentifiersList, personHasMultipleGenomes, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(personAnalysisObject)
if (err != nil) { return err }
allGenomeIdentifiersList := allRawGenomeIdentifiersList
@ -859,7 +943,7 @@ func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnal
for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier)
_, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier)
if (err != nil) { return err }
}
@ -886,27 +970,31 @@ func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnal
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, _, _, _, err := GetPersonTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
if (err != nil) { return err }
}
traitRulesList := traitObject.RulesList
for _, traitRuleObject := range traitRulesList{
ruleIdentifierHex := traitRuleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
if (traitIsDiscreteOrNumeric == "Discrete"){
for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, err := GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
_, _, _, _, _, _, _, _, _, _, _, _, _, _, err := GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
if (err != nil) { return err }
}
traitRulesList := traitObject.RulesList
for _, traitRuleObject := range traitRulesList{
ruleIdentifierHex := traitRuleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, err := GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(personAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil) { return err }
}
}
}
}
@ -998,27 +1086,31 @@ func VerifyCoupleGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnal
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
for _, genomePairIdentifier := range allGenomePairIdentifiersList{
_, _, _, _, err := GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
if (err != nil) { return err }
}
traitRulesList := traitObject.RulesList
for _, traitRuleObject := range traitRulesList{
ruleIdentifierHex := traitRuleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
if (traitIsDiscreteOrNumeric == "Discrete"){
for _, genomePairIdentifier := range allGenomePairIdentifiersList{
_, _, _, err := GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
_, _, _, _, _, _, _, _, _, _, _, _, _, err := GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
if (err != nil) { return err }
}
traitRulesList := traitObject.RulesList
for _, traitRuleObject := range traitRulesList{
ruleIdentifierHex := traitRuleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
for _, genomePairIdentifier := range allGenomePairIdentifiersList{
_, _, _, err := GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
if (err != nil) { return err }
}
}
}
}

View file

@ -128,41 +128,44 @@ func ReadRawGenomeFile(fileReader io.Reader) (string, int, int64, int64, bool, m
}
getMonthObject := func()(time.Month, error){
if (monthString == "01"){
return time.January, nil
}
if (monthString == "02"){
return time.February, nil
}
if (monthString == "03"){
return time.March, nil
}
if (monthString == "04"){
return time.April, nil
}
if (monthString == "05"){
return time.May, nil
}
if (monthString == "06"){
return time.June, nil
}
if (monthString == "07"){
return time.July, nil
}
if (monthString == "08"){
return time.August, nil
}
if (monthString == "09"){
return time.September, nil
}
if (monthString == "10"){
return time.October, nil
}
if (monthString == "11"){
return time.November, nil
}
if (monthString == "12"){
return time.December, nil
switch monthString{
case "01":{
return time.January, nil
}
case "02":{
return time.February, nil
}
case "03":{
return time.March, nil
}
case "04":{
return time.April, nil
}
case "05":{
return time.May, nil
}
case "06":{
return time.June, nil
}
case "07":{
return time.July, nil
}
case "08":{
return time.August, nil
}
case "09":{
return time.September, nil
}
case "10":{
return time.October, nil
}
case "11":{
return time.November, nil
}
case "12":{
return time.December, nil
}
}
return time.January, errors.New("Malformed AncestryDNA genome file: Invalid month: " + monthString)
}
@ -438,15 +441,22 @@ func ReadRawGenomeFile(fileReader io.Reader) (string, int, int64, int64, bool, m
snpIdentifier := rowSlice[0]
snpValueRaw := rowSlice[3]
if (snpValueRaw[0] != byte('-')){
// Locus value is not "--"
// Locus value exists
numberOfLoci += 1
if (len(snpValueRaw) < 2){
return "", 0, 0, 0, false, nil, errors.New("Malformed 23andMe genome data: Invalid SNP row snp value: " + fileLineString)
}
if (snpValueRaw[0] == '-'){
// Locus value is "--"
// Locus value does not exist
continue
}
numberOfLoci += 1
//Outputs:
// -bool: rsID found
// -int64: rsID value
// -error
getRSIDIdentifier := func()(bool, int64, error){
isRSID, rsidInt64 := readRSIDString(snpIdentifier)
@ -481,75 +491,50 @@ func ReadRawGenomeFile(fileReader io.Reader) (string, int, int64, int64, bool, m
continue
}
// This will return either a base pair or a single base
// Base pair can be "--"
getLocusValueString := func()(string, error){
getLocusBasesList := func()([]rune, error){
// This value has a control character suffix
// Final index is always a control character
// We remove the control character suffix
locusBasesList := make([]rune, 0)
if (len(snpValueRaw) == 2){
finalIndex := len(snpValueRaw) - 1
singleBase := string(snpValueRaw[0])
return singleBase, nil
}
for index, character := range snpValueRaw{
if (len(snpValueRaw) == 3){
baseIsValid := verifyBase(string(character))
if (baseIsValid == false){
basePair := snpValueRaw[:2]
return basePair, nil
}
if (index == finalIndex){
// The final index of snpValueRaw is sometimes a control character
return "", errors.New("Malformed 23andMe genome file: Invalid SNP value: " + snpValueRaw)
}
return locusBasesList, nil
}
basesString, err := getLocusValueString()
if (err != nil) { return "", 0, 0, 0, false, nil, err }
if (basesString == "--"){
// No data exists, skip.
continue
}
for _, baseRune := range basesString{
baseIsValid := verifyBase(string(baseRune))
if (baseIsValid == false){
return "", 0, 0, 0, false, nil, errors.New("Malformed 23andMe genome file: Invalid SNP base: " + string(baseRune))
}
}
getMapEntryValue := func()RawGenomeLocusValue{
if (len(basesString) == 1){
locusValueObject := RawGenomeLocusValue{
Allele1: basesString,
Allele2Exists: false,
Allele2: "",
return nil, errors.New("Malformed 23andMe genome file: Invalid SNP base: " + string(character))
}
return locusValueObject
locusBasesList = append(locusBasesList, character)
}
baseAString := string(basesString[0])
baseBString := string(basesString[1])
locusValueObject := RawGenomeLocusValue{
Allele1: baseAString,
Allele2Exists: true,
Allele2: baseBString,
}
return locusValueObject
return locusBasesList, nil
}
mapEntryValue := getMapEntryValue()
locusBasesList, err := getLocusBasesList()
if (err != nil){ return "", 0, 0, 0, false, nil, err }
genomeMap[locusRSID] = mapEntryValue
allele1 := string(locusBasesList[0])
locusValueObject := RawGenomeLocusValue{
Allele1: allele1,
}
if (len(locusBasesList) > 1){
allele2 := string(locusBasesList[1])
locusValueObject.Allele2Exists = true
locusValueObject.Allele2 = allele2
}
genomeMap[locusRSID] = locusValueObject
}
return "23andMe", 1, fileTimeUnix, numberOfLoci, false, genomeMap, nil

View file

@ -769,6 +769,14 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
return false, profileVersion, "", nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return false, 0, "", err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, 0, "", errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
}
polygenicDiseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList()
if (err != nil) { return false, 0, "", err }
@ -780,12 +788,8 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
for _, diseaseObject := range polygenicDiseaseObjectsList{
diseaseName := diseaseObject.DiseaseName
diseaseLociList := diseaseObject.LociList
_, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, myGenomeIdentifier)
if (err != nil) { return false, 0, "", err }
// Map Structure: rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
@ -818,7 +822,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
userDiseaseLocusValuesMap[locusRSID] = newLocusValue
}
anyLocusValuesTested, offspringAverageRiskScore, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo_Fast(diseaseLociList, myDiseaseLocusValuesMap, userDiseaseLocusValuesMap)
anyLocusValuesTested, offspringAverageRiskScore, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo_Fast(diseaseLociList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) { return false, 0, "", err }
if (anyLocusValuesTested == false){
continue
@ -1399,9 +1403,14 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
traitName := getTraitName()
myTraitLociMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myGeneticAnalysisObject, traitName, myGenomeIdentifier)
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return false, 0, "", err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, 0, "", errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis with a GenomesMap which is missing my genome identifier.")
}
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return false, 0, "", err }
@ -1415,7 +1424,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
for _, rsID := range traitLociList{
myLocusValue, myLocusValueExists := myTraitLociMap[rsID]
myLocusValue, myLocusValueExists := myGenomeLocusValuesMap[rsID]
if (myLocusValueExists == false){
continue
}

View file

@ -150,6 +150,14 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
return errors.New("UpdateMyExportedProfile called when profile genetic analysis is not ready.")
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return err }
myGenomeLocusValuesMap, exists := myGenomesMap[genomeIdentifierToShare]
if (exists == false){
return errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
}
monogenicDiseaseNamesList, err := monogenicDiseases.GetMonogenicDiseaseNamesList()
if (err != nil) { return err }
@ -205,19 +213,24 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
continue
}
_, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, genomeIdentifierToShare)
if (err != nil) { return err }
lociList := diseaseObject.LociList
for rsID, locusValueObject := range myDiseaseLocusValuesMap{
for _, locusObject := range lociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
locusRSID := locusObject.LocusRSID
locusBase1 := locusValueObject.Base1Value
locusBase2 := locusValueObject.Base2Value
locusValueObject, exists := myGenomeLocusValuesMap[locusRSID]
if (exists == true){
basePairValue := locusBase1 + ";" + locusBase2
rsIDString := helpers.ConvertInt64ToString(locusRSID)
profileMap["LocusValue_rs" + rsIDString] = basePairValue
locusBase1 := locusValueObject.Base1Value
locusBase2 := locusValueObject.Base2Value
basePairValue := locusBase1 + ";" + locusBase2
profileMap["LocusValue_rs" + rsIDString] = basePairValue
}
}
}
@ -239,19 +252,22 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
continue
}
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myGeneticAnalysisObject, traitName, genomeIdentifierToShare)
if (err != nil) { return err }
lociList := traitObject.LociList
for rsID, locusValueObject := range myTraitLocusValuesMap{
for _, rsID := range lociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
locusValueObject, exists := myGenomeLocusValuesMap[rsID]
if (exists == true){
locusBase1 := locusValueObject.Base1Value
locusBase2 := locusValueObject.Base2Value
rsIDString := helpers.ConvertInt64ToString(rsID)
basePairValue := locusBase1 + ";" + locusBase2
locusBase1 := locusValueObject.Base1Value
locusBase2 := locusValueObject.Base2Value
profileMap["LocusValue_rs" + rsIDString] = basePairValue
basePairValue := locusBase1 + ";" + locusBase2
profileMap["LocusValue_rs" + rsIDString] = basePairValue
}
}
}

View file

@ -0,0 +1,58 @@
// geneticPredictionModels contains genetic prediction neural network models for predicting genetic traits
// These are .gob encoded files of []float32 weights
// This package also contains prediction accuracy information for each model
// Prediction accuracy models describe information about how accurate the predictions made by the models are
// All of the files in this package are created by the Create Genetic Models utility.
// This utility is located in /utilities/createGeneticModels/createGeneticModels.go
package geneticPredictionModels
import _ "embed"
import "errors"
//Outputs:
// -bool: Model exists
// -[]byte
func GetGeneticPredictionModelBytes(traitName string)(bool, []byte){
switch traitName{
case "Eye Color":{
return true, predictionModel_EyeColor
}
case "Lactose Tolerance":{
return true, predictionModel_LactoseTolerance
}
}
return false, nil
}
//go:embed predictionModels/EyeColorModel.gob
var predictionModel_EyeColor []byte
//go:embed predictionModels/LactoseToleranceModel.gob
var predictionModel_LactoseTolerance []byte
// The files returned by this function are .gob encoded geneticPrediction.TraitPredictionAccuracyInfoMap objects
func GetPredictionModelTraitAccuracyInfoBytes(traitName string)([]byte, error){
switch traitName{
case "Eye Color":{
return predictionAccuracy_EyeColor, nil
}
case "Lactose Tolerance":{
return predictionAccuracy_LactoseTolerance, nil
}
}
return nil, errors.New("GetPredictionModelTraitAccuracyInfoFile called with unknown traitName: " + traitName)
}
//go:embed predictionModelAccuracies/EyeColorModelAccuracy.gob
var predictionAccuracy_EyeColor []byte
//go:embed predictionModelAccuracies/LactoseToleranceModelAccuracy.gob
var predictionAccuracy_LactoseTolerance []byte

View file

@ -0,0 +1,48 @@
package geneticPredictionModels_test
import "seekia/resources/geneticPredictionModels"
import "testing"
import "seekia/internal/genetics/geneticPrediction"
func TestGeneticPredictionModels(t *testing.T){
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
for _, traitName := range traitNamesList{
modelFound, modelBytes := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
if (modelFound == false){
t.Fatalf("GetGeneticPredictionModelBytes failed to find model for trait: " + traitName)
}
_, err := geneticPrediction.DecodeBytesToNeuralNetworkObject(modelBytes)
if (err != nil){
t.Fatalf("DecodeBytesToNeuralNetworkObject failed: " + err.Error())
}
}
}
func TestGeneticPredictionModelAccuracies(t *testing.T){
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
for _, traitName := range traitNamesList{
accuracyInfoBytes, err := geneticPredictionModels.GetPredictionModelTraitAccuracyInfoBytes(traitName)
if (err != nil){
t.Fatalf("GetGeneticPredictionModelBytes failed: " + err.Error())
}
_, err = geneticPrediction.DecodeBytesToTraitPredictionAccuracyInfoMap(accuracyInfoBytes)
if (err != nil){
t.Fatalf("DecodeBytesToTraitPredictionAccuracyInfoMap failed: " + err.Error())
}
}
}

View file

@ -309,10 +309,13 @@ func TestGeneticReferences(t *testing.T){
traitName := traitObject.TraitName
traitDescription := traitObject.TraitDescription
traitDiscreteOrNumeric := traitObject.DiscreteOrNumeric
traitLocusReferencesMap := traitObject.LocusReferencesMap
traitLociList := traitObject.LociList
traitLociList_Rules := traitObject.LociList_Rules
traitRulesList := traitObject.RulesList
traitOutcomesList := traitObject.OutcomesList
traitReferencesMap := traitObject.References
traitReferencesMap := traitObject.ReferencesMap
if (traitName == ""){
t.Fatalf("Empty trait name exists.")
@ -326,6 +329,9 @@ func TestGeneticReferences(t *testing.T){
if (traitDescription == ""){
t.Fatalf("Empty trait description exists for trait: " + traitName)
}
if (traitDiscreteOrNumeric != "Discrete" && traitDiscreteOrNumeric != "Numeric"){
t.Fatalf("Invalid DiscreteOrNumeric for trait: " + traitDiscreteOrNumeric)
}
if (len(traitOutcomesList) != 0){
if (len(traitOutcomesList) < 2){
@ -349,18 +355,41 @@ func TestGeneticReferences(t *testing.T){
t.Fatalf("Invalid references exist for trait: " + traitName)
}
if (len(traitLocusReferencesMap) == 0){
t.Fatalf("No trait locus references exist for trait: " + traitName)
}
for locusRSID, locusReferences := range traitLocusReferencesMap{
allRSIDsMap[locusRSID] = struct{}{}
if (locusReferences == nil){
t.Fatalf("A trait locus has no references map: " + traitName)
}
if (len(locusReferences) == 0){
t.Fatalf("A trait locus has no references: " + traitName)
}
locusExists := slices.Contains(traitLociList, locusRSID)
if (locusExists == false){
t.Fatalf("traitLocusReferencesMap contains rsID which does not exist in traitLociList")
}
}
if (len(traitLociList) == 0){
t.Fatalf("No trait loci exist for trait: " + traitName)
}
for _, locusRSID := range traitLociList{
allRSIDsMap[locusRSID] = struct{}{}
for _, rsID := range traitLociList{
allRSIDsMap[rsID] = struct{}{}
}
containsDuplicates, duplicateLocus := helpers.CheckIfListContainsDuplicates(traitLociList)
if (containsDuplicates == true){
duplicateLocusString := helpers.ConvertInt64ToString(duplicateLocus)
t.Fatalf("traitLociList contains duplicates for trait: " + traitName + ". RSID: " + duplicateLocusString)
for _, rsID := range traitLociList_Rules{
locusExists := slices.Contains(traitLociList, rsID)
if (locusExists == false){
t.Fatalf("traitLociList_Rules contains locus not present in traitLociList")
}
}
if (len(traitRulesList) == 0){
@ -373,7 +402,7 @@ func TestGeneticReferences(t *testing.T){
ruleIdentifier := ruleObject.RuleIdentifier
ruleLociList := ruleObject.LociList
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
ruleReferences := ruleObject.References
ruleReferencesMap := ruleObject.ReferencesMap
identifierIsValid := verifyIdentifier(ruleIdentifier)
if (identifierIsValid == false){
@ -413,11 +442,21 @@ func TestGeneticReferences(t *testing.T){
t.Fatalf("Trait rule Locus identifier is invalid: " + locusIdentifier)
}
listContainsItem := slices.Contains(traitLociList, locusRSID)
if (listContainsItem == false){
_, mapContainsItem := traitLocusReferencesMap[locusRSID]
if (mapContainsItem == false){
t.Fatalf("Rule locus contains rsid which is not contained within LocusReferencesMap.")
}
sliceContainsItem := slices.Contains(traitLociList, locusRSID)
if (sliceContainsItem == false){
t.Fatalf("Rule locus contains rsid which is not contained within traitLociList.")
}
sliceContainsItem = slices.Contains(traitLociList_Rules, locusRSID)
if (sliceContainsItem == false){
t.Fatalf("Rule locus contains rsid which is not contained within traitLociList_Rules.")
}
if (len(locusBasePairsList) == 0){
t.Fatalf("Trait rule locus base pairs list is empty: " + locusIdentifier)
}
@ -430,7 +469,7 @@ func TestGeneticReferences(t *testing.T){
}
}
referencesAreValid := verifyReferencesMap(ruleReferences)
referencesAreValid := verifyReferencesMap(ruleReferencesMap)
if (referencesAreValid == false){
t.Fatalf("Invalid references map for trait rule locus: " + ruleIdentifier)
}

View file

@ -8,9 +8,8 @@ package polygenicDiseases
// Polygenic disease probabilities are less accurate, because individual base pair changes only cause comparatively small changes in the disease risk.
// Polygenic diseases are also more influenced by environmental factors, further decreasing risk accuracy.
//TODO: Eventually we want to use neural networks for both polygenic disease and trait prediction.
// This package is currently a temporary, less accurate solution until we get access to the necessary training data.
// It may still be worth keeping this method in place for polygenic diseases and using neural networks in tandem.
//TODO: Eventually we want to use neural networks for polygenic disease prediction.
// This package is currently a less accurate solution until we get access to the necessary training data.
import "errors"

View file

@ -1,14 +1,20 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getEyeColorTraitObject()Trait{
eyeColorLociList := []int64{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
//TODO: Add more SNPs.
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - Eye Color"] = "https://www.snpedia.com/index.php/Eye_color"
// These SNPs are taken from https://www.snpedia.com/index.php/Eye_color
// These SNPs are taken from https://www.snpedia.com/index.php/Eye_color
lociList_1 := []int64{
2733832,
1800401,
1800407,
@ -51,16 +57,39 @@ func getEyeColorTraitObject()Trait{
989869,
4778138,
12906280,
}
// These SNPs are taken from https://pubmed.ncbi.nlm.nih.gov/20546537/
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
referencesMap_List2 := make(map[string]string)
referencesMap_List2["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
// These SNPs are taken from https://pubmed.ncbi.nlm.nih.gov/20546537/
lociList_2 := []int64{
12203592,
1408799,
1126809,
12896399,
7495174,
1667394,
}
// These SNPs are taken from https://pubmed.ncbi.nlm.nih.gov/33692100/
for _, rsID := range lociList_2{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List2)
}
referencesMap_List3 := make(map[string]string)
referencesMap_List3["Genome-wide association study in almost 195,000 individuals identifies 50 previously unidentified genetic loci for eye color."] = "https://pubmed.ncbi.nlm.nih.gov/33692100/"
// These SNPs are taken from https://pubmed.ncbi.nlm.nih.gov/33692100/
lociList_3 := []int64{
6693258,
351385,
2385028,
@ -115,6 +144,12 @@ func getEyeColorTraitObject()Trait{
// 5957354,
}
for _, rsID := range lociList_3{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List3)
}
eyeColorLociList := helpers.GetListOfMapKeys(locusReferencesMap)
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Eye Color"] = "https://www.snpedia.com/index.php/Eye_color"
referencesMap["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
@ -123,10 +158,13 @@ func getEyeColorTraitObject()Trait{
eyeColorObject := Trait{
TraitName: "Eye Color",
TraitDescription: "The color of a person's eyes.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: eyeColorLociList,
LociList_Rules: []int64{},
RulesList: []TraitRule{},
OutcomesList: []string{"Blue", "Green", "Hazel", "Brown"},
References: referencesMap,
ReferencesMap: referencesMap,
}
return eyeColorObject

View file

@ -1,9 +1,19 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getFacialStructureTraitObject()Trait{
facialStructureLociList := []int64{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap_List1["A Genome-Wide Association Study Identifies Five Loci Influencing Facial Morphology in Europeans"] = "https://journals.plos.org/plosgenetics/article?id=10.1371/journal.pgen.1002932"
lociList_1 := []int64{
//TODO: Add more SNPs.
@ -112,6 +122,13 @@ func getFacialStructureTraitObject()Trait{
397723,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
facialStructureLociList := helpers.GetListOfMapKeys(locusReferencesMap)
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap["A Genome-Wide Association Study Identifies Five Loci Influencing Facial Morphology in Europeans"] = "https://journals.plos.org/plosgenetics/article?id=10.1371/journal.pgen.1002932"
@ -119,10 +136,13 @@ func getFacialStructureTraitObject()Trait{
facialStructureObject := Trait{
TraitName: "Facial Structure",
TraitDescription: "The structure of a person's face.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: facialStructureLociList,
LociList_Rules: []int64{},
RulesList: []TraitRule{},
OutcomesList: []string{},
References: referencesMap,
ReferencesMap: referencesMap,
}
return facialStructureObject

View file

@ -3,10 +3,20 @@ package traits
// Hair color is influenced by thousands of genes
// We only have a few listed here
import "seekia/internal/helpers"
import "maps"
func getHairColorTraitObject()Trait{
hairColorLociList := []int64{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap_List1["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
lociList_1 := []int64{
//These loci were taken from https://pubmed.ncbi.nlm.nih.gov/20546537/
@ -32,6 +42,13 @@ func getHairColorTraitObject()Trait{
1805008,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
hairColorLociList := helpers.GetListOfMapKeys(locusReferencesMap)
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
@ -39,10 +56,13 @@ func getHairColorTraitObject()Trait{
hairColorObject := Trait{
TraitName: "Hair Color",
TraitDescription: "The color of a person's hair.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: hairColorLociList,
LociList_Rules: []int64{},
RulesList: []TraitRule{},
OutcomesList: []string{},
References: referencesMap,
ReferencesMap: referencesMap,
}
return hairColorObject

View file

@ -1,11 +1,53 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getHairTextureTraitObject()Trait{
rule1_ReferencesMap := make(map[string]string)
rule1_ReferencesMap["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
lociList_1 := []int64{
7349332,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
referencesMap_List2 := make(map[string]string)
referencesMap_List2["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
lociList_2 := []int64{
11803731,
}
for _, rsID := range lociList_2{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List2)
}
referencesMap_List3 := make(map[string]string)
referencesMap_List3["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
lociList_3 := []int64{
17646946,
}
for _, rsID := range lociList_3{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List3)
}
referencesMap_rs7349332 := make(map[string]string)
referencesMap_rs7349332["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
rule1_Locus1Object := RuleLocus{
@ -22,12 +64,10 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "fde405",
LociList: rule1_LociList,
OutcomePointsMap: rule1_OutcomePointsMap,
References: rule1_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs7349332),
}
//TODO: Make sure this is true, that a heterozygote has a higher likelihood of curly hair
rule2_ReferencesMap := make(map[string]string)
rule2_ReferencesMap["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
rule2_Locus1Object := RuleLocus{
@ -44,12 +84,9 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "6bd1da",
LociList: rule2_LociList,
OutcomePointsMap: rule2_OutcomePointsMap,
References: rule2_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs7349332),
}
rule3_ReferencesMap := make(map[string]string)
rule3_ReferencesMap["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
rule3_Locus1Object := RuleLocus{
LocusIdentifier: "c6760e",
@ -65,11 +102,13 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "32e377",
LociList: rule3_LociList,
OutcomePointsMap: rule3_OutcomePointsMap,
References: rule3_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs7349332),
}
rule4_ReferencesMap := make(map[string]string)
rule4_ReferencesMap["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
referencesMap_rs11803731 := make(map[string]string)
referencesMap_rs11803731["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
rule4_Locus1Object := RuleLocus{
@ -86,12 +125,10 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "34e6d2",
LociList: rule4_LociList,
OutcomePointsMap: rule4_OutcomePointsMap,
References: rule4_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs11803731),
}
//TODO: Make sure this is true, that a heterozygote has a higher likelihood of curly hair
rule5_ReferencesMap := make(map[string]string)
rule5_ReferencesMap["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
rule5_Locus1Object := RuleLocus{
@ -108,12 +145,9 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "cf6cb5",
LociList: rule5_LociList,
OutcomePointsMap: rule5_OutcomePointsMap,
References: rule5_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs11803731),
}
rule6_ReferencesMap := make(map[string]string)
rule6_ReferencesMap["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
rule6_Locus1Object := RuleLocus{
LocusIdentifier: "f554b5",
@ -129,11 +163,11 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "2ba65b",
LociList: rule6_LociList,
OutcomePointsMap: rule6_OutcomePointsMap,
References: rule6_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs11803731),
}
rule7_ReferencesMap := make(map[string]string)
rule7_ReferencesMap["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
referencesMap_rs17646946 := make(map[string]string)
referencesMap_rs17646946["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
rule7_Locus1Object := RuleLocus{
@ -150,12 +184,9 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "ae3274",
LociList: rule7_LociList,
OutcomePointsMap: rule7_OutcomePointsMap,
References: rule7_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs17646946),
}
rule8_ReferencesMap := make(map[string]string)
rule8_ReferencesMap["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
rule8_Locus1Object := RuleLocus{
LocusIdentifier: "f1144a",
@ -171,12 +202,9 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "a546bf",
LociList: rule8_LociList,
OutcomePointsMap: rule8_OutcomePointsMap,
References: rule8_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs17646946),
}
rule9_ReferencesMap := make(map[string]string)
rule9_ReferencesMap["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
rule9_Locus1Object := RuleLocus{
LocusIdentifier: "468bb3",
@ -192,26 +220,32 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "b8dc0a",
LociList: rule9_LociList,
OutcomePointsMap: rule9_OutcomePointsMap,
References: rule9_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs17646946),
}
hairTextureRulesList := []TraitRule{rule1_Object, rule2_Object, rule3_Object, rule4_Object, rule5_Object, rule6_Object, rule7_Object, rule8_Object, rule9_Object}
hairTextureLociList := []int64{17646946, 11803731, 7349332}
lociList_Rules := []int64{7349332, 11803731, 17646946}
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Hair Curliness"] = "https://www.snpedia.com/index.php/Hair_curliness"
outcomesList := []string{"Straight", "Curly"}
hairTextureLociList := helpers.GetListOfMapKeys(locusReferencesMap)
hairTextureObject := Trait{
TraitName: "Hair Texture",
TraitDescription: "The texture of a person's head hair.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: hairTextureLociList,
LociList_Rules: lociList_Rules,
RulesList: hairTextureRulesList,
OutcomesList: outcomesList,
References: referencesMap,
ReferencesMap: referencesMap,
}
return hairTextureObject

View file

@ -1,11 +1,30 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getLactoseToleranceTraitObject()Trait{
rule1_ReferencesMap := make(map[string]string)
rule1_ReferencesMap["SNPedia.com - rs182549"] = "https://www.snpedia.com/index.php/Rs182549"
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_1 := make(map[string]string)
referencesMap_1["SNPedia.com - rs182549"] = "https://www.snpedia.com/index.php/Rs182549"
locusReferencesMap[182549] = referencesMap_1
referencesMap_2 := make(map[string]string)
referencesMap_2["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
locusReferencesMap[4988235] = referencesMap_2
referencesMap_rs182549 := make(map[string]string)
referencesMap_rs182549["SNPedia.com - rs182549"] = "https://www.snpedia.com/index.php/Rs182549"
rule1_Locus1Object := RuleLocus{
@ -22,12 +41,9 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "f4e02c",
LociList: rule1_LociList,
OutcomePointsMap: rule1_OutcomePointsMap,
References: rule1_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs182549),
}
rule2_ReferencesMap := make(map[string]string)
rule2_ReferencesMap["SNPedia.com - rs182549"] = "https://www.snpedia.com/index.php/Rs182549"
rule2_Locus1Object := RuleLocus{
LocusIdentifier: "a7feff",
@ -43,11 +59,11 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "cc3df0",
LociList: rule2_LociList,
OutcomePointsMap: rule2_OutcomePointsMap,
References: rule2_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs182549),
}
rule3_ReferencesMap := make(map[string]string)
rule3_ReferencesMap["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
referencesMap_rs4988235 := make(map[string]string)
referencesMap_rs4988235["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
rule3_Locus1Object := RuleLocus{
@ -64,12 +80,9 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "8170ee",
LociList: rule3_LociList,
OutcomePointsMap: rule3_OutcomePointsMap,
References: rule3_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs4988235),
}
rule4_ReferencesMap := make(map[string]string)
rule4_ReferencesMap["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
rule4_Locus1Object := RuleLocus{
LocusIdentifier: "176dde",
@ -85,12 +98,9 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "52425f",
LociList: rule4_LociList,
OutcomePointsMap: rule4_OutcomePointsMap,
References: rule4_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs4988235),
}
rule5_ReferencesMap := make(map[string]string)
rule5_ReferencesMap["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
rule5_Locus1Object := RuleLocus{
LocusIdentifier: "164acb",
@ -106,25 +116,31 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "4b5c35",
LociList: rule5_LociList,
OutcomePointsMap: rule5_OutcomePointsMap,
References: rule5_ReferencesMap,
ReferencesMap: maps.Clone(referencesMap_rs4988235),
}
lactoseToleranceRulesList := []TraitRule{rule1_Object, rule2_Object, rule3_Object, rule4_Object, rule5_Object}
lactoseToleranceLociList := []int64{4988235, 182549}
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Lactose Intolerance"] = "https://www.snpedia.com/index.php/Lactose_intolerance"
outcomesList := []string{"Tolerant", "Intolerant"}
lactoseToleranceLociList := helpers.GetListOfMapKeys(locusReferencesMap)
lociList_Rules := []int64{182549, 4988235}
lactoseToleranceObject := Trait{
TraitName: "Lactose Tolerance",
TraitDescription: "The ability to tolerate lactose.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: lactoseToleranceLociList,
LociList_Rules: lociList_Rules,
RulesList: lactoseToleranceRulesList,
OutcomesList: outcomesList,
References: referencesMap,
ReferencesMap: referencesMap,
}
return lactoseToleranceObject

View file

@ -1,10 +1,18 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getSkinColorTraitObject()Trait{
skinColorLociList := []int64{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
lociList_1 := []int64{
//TODO: Add more SNPs.
@ -14,12 +22,32 @@ func getSkinColorTraitObject()Trait{
26722,
1426654,
642742,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
referencesMap_List2 := make(map[string]string)
referencesMap_List2["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
lociList_2 := []int64{
// These SNPs are from https://pubmed.ncbi.nlm.nih.gov/20546537/
16891982,
12203592,
1042602,
1834640,
}
for _, rsID := range lociList_2{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List2)
}
referencesMap_List3 := make(map[string]string)
referencesMap_List3["Meta-analysis and prioritization of human skin pigmentation-associated GWAS-SNPs using ENCODE data-based web-tools"] = "https://link.springer.com/article/10.1007/s00403-019-01891-3"
lociList_3 := []int64{
// These SNPs are from https://link.springer.com/article/10.1007/s00403-019-01891-3
7182710,
@ -33,18 +61,28 @@ func getSkinColorTraitObject()Trait{
3212368,
}
for _, rsID := range lociList_3{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List3)
}
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
referencesMap["Meta-analysis and prioritization of human skin pigmentation-associated GWAS-SNPs using ENCODE data-based web-tools"] = "https://link.springer.com/article/10.1007/s00403-019-01891-3"
lociList := helpers.GetListOfMapKeys(locusReferencesMap)
skinColorObject := Trait{
TraitName: "Skin Color",
TraitDescription: "The color of a person's skin.",
LociList: skinColorLociList,
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: lociList,
LociList_Rules: []int64{},
RulesList: []TraitRule{},
OutcomesList: []string{},
References: referencesMap,
ReferencesMap: referencesMap,
}
return skinColorObject

View file

@ -3,30 +3,51 @@
package traits
// TODO: We want to eventually use neural nets for both trait and polygenic disease analysis
// These will be trained on a set of genomes and will output a probability analysis for each trait
// This is only possible once we get access to the necessary training data
//
// See geneticPrediction.go for a non-working attempt to predict traits with neural nets
import "errors"
type RuleLocus struct{
type Trait struct{
// 3 byte hex encoded string
LocusIdentifier string
// Example: "Eye Color"
TraitName string
// RSID that represents this locus
// If multiple RSIDs represent the same locus, use the first rsid for the locus in the locusMetadata package
LocusRSID int64
TraitDescription string
// List of base pair values that this RSID must fulfill to pass the rule
// As long as the value matches any base pair value in the list, the genome has passed this rule locus
// The genome must pass every rule locus within a rule to pass the rule
BasePairsList []string
// This describes if the trait is discrete or numeric
// Discrete traits have a set of outcomes (Example: Eye Color: Blue, Green...)
// Numeric traits have a numeric outcome (Example: Height)
// The value of this variable is either "Discrete" or "Numeric"
DiscreteOrNumeric string
// This is a list of rsIDs which are known to have an effect on this trait
// These loci may not have any associated rules
// We use these loci to predict trait outcomes with neural networks.
// We also use these loci to calculate Racial Similarity.
// Map Structure: rsID -> (map[ReferenceName]Reference Link)
LocusReferencesMap map[int64]map[string]string
// This is a list of all loci used to predict this trait
// If a neural network exists, all of these will be used as input into the network for prediction
LociList []int64
// This is a list of all loci used to predict this trait using rules
// It is sometimes a subset of LociList
LociList_Rules []int64
// This list can be empty if no rules exist
// An empty list means we are relying on LociList and neural networks for trait prediction.
RulesList []TraitRule
// List of outcomes
// Example: "Lactose Intolerant", "Lactore Tolerant"
// This list can be empty if outcomes are not text descriptions (Example: Facial structure)
// If the trait is Numeric, or their or no rules nor a neural network, then this list will be empty.
OutcomesList []string
// This map contains scientific resources about this trait
// Map structure: Reference name -> Reference link
ReferencesMap map[string]string
}
type TraitRule struct{
// 3 byte identifier encoded hex
@ -44,36 +65,26 @@ type TraitRule struct{
OutcomePointsMap map[string]int
// Map structure: Reference name -> Reference link
References map[string]string
ReferencesMap map[string]string
}
type Trait struct{
type RuleLocus struct{
TraitName string
// 3 byte hex encoded string
LocusIdentifier string
TraitDescription string
// RSID that represents this locus
// If multiple RSIDs represent the same locus, use the first rsid for the locus in the locusMetadata package
LocusRSID int64
// This is a list of rsIDs which are known to have an effect on this trait
// These loci may not have any associated rules
// We use these loci to calculate Racial Similarity.
// We will also use neural networks to predict trait outcome scores using these loci
LociList []int64
// This list can be empty if no rules exist
// An empty list means we are relying on LociList and neural networks for trait prediction.
RulesList []TraitRule
// List of outcomes
// Example: "Lactose Intolerant", "Lactore Tolerant"
// This list can be empty if outcomes are not text descriptions (Example: Facial structure)
// If there are no outcomes, then no rules can exist
OutcomesList []string
// Map structure: Reference name -> Reference link
References map[string]string
// List of base pair values that this RSID must fulfill to pass the rule
// As long as the value matches any base pair value in the list, the genome has passed this rule locus
// The genome must pass every rule locus within a rule to pass the rule
BasePairsList []string
}
var traitNamesList []string
var traitObjectsList []Trait

View file

@ -1,3 +1,4 @@
OpenSNPDataArchiveFolderpath.txt
TrainingData
TrainedModels
ModelAccuracies

View file

@ -3,6 +3,7 @@
// These are neural networks which predict traits such as eye color from raw genome files
// The OpenSNP.org dataset is used, and more datasets will be added in the future.
// You must download the dataset and extract it. The instructions are described in the utility.
// The trained models are saved in the /resources/geneticPredictionModels package for use in the Seekia app.
package main
@ -1136,10 +1137,16 @@ func setStartAndMonitorTrainModelPage(window fyne.Window, traitName string, prev
_, err := localFilesystem.CreateFolder("./TrainedModels")
if (err != nil) { return false, err }
trainingSetFilepathsList, _, err := getTrainingAndTestingDataFilepathLists(traitName)
if (err != nil) { return false, err }
// Now we deterministically randomize the order of the trainingSetFilepathsList
pseudorandomNumberGenerator := mathRand.New(mathRand.NewPCG(1, 2))
pseudorandomNumberGenerator.Shuffle(len(trainingSetFilepathsList), func(i int, j int){
trainingSetFilepathsList[i], trainingSetFilepathsList[j] = trainingSetFilepathsList[j], trainingSetFilepathsList[i]
})
// We create a new neural network object to train
neuralNetworkObject, err := geneticPrediction.GetNewUntrainedNeuralNetworkObject(traitName)
if (err != nil) { return false, err }
@ -1277,7 +1284,8 @@ func setTestModelsPage(window fyne.Window, previousPage func()){
description2 := getLabelCentered("This will test each neural network using user training data examples.")
description3 := getLabelCentered("The testing data is not used to train the models.")
description4 := getLabelCentered("The results of the testing will be displayed at the end.")
description5 := getLabelCentered("You must select a trait model to test.")
description5 := getLabelCentered("The results will also be saved in the ModelAccuracies folder.")
description6 := getLabelCentered("You must select a trait model to test.")
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
@ -1301,50 +1309,12 @@ func setTestModelsPage(window fyne.Window, previousPage func()){
traitNameSelectorCentered := getWidgetCentered(traitNameSelector)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, widget.NewSeparator(), traitNameSelectorCentered, widget.NewSeparator(), beginTestingButton)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, widget.NewSeparator(), traitNameSelectorCentered, widget.NewSeparator(), beginTestingButton)
window.SetContent(page)
}
type TraitOutcomeInfo struct{
// This is the outcome which was found
// Example: "Blue"
OutcomeName string
// This is a value between 0-100 which describes the percentage of the loci which were tested for the input for the prediction
PercentageOfLociTested int
// This is a value between 0-100 which describes the percentage of the tested loci which were phased for the input for the prediction
PercentageOfPhasedLoci int
}
type TraitPredictionAccuracyInfo struct{
// This contains the quantity of examples for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
QuantityOfExamples int
// This contains the quantity of predictions for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
// Prediction = our model predicted this outcome
QuantityOfPredictions int
// This stores the probability (0-100) that our model will accurately predict this outcome for a genome which has
// the specified percentageOfLociTested and percentageOfPhasedLoci
// In other words: What is the probability that if you give Seekia a blue-eyed genome, it will give you a correct Blue prediction?
// This value is only accurate is QuantityOfExamples > 0
ProbabilityOfCorrectGenomePrediction int
// This stores the probability (0-100) that our model is correct if our model predicts that a genome
// with the specified percentageOfLociTested and percentageOfPhasedLoci has this outcome
// In other words: What is the probability that if Seekia says a genome will have blue eyes, it is correct?
// This value is only accurate is QuantityOfPredictions > 0
ProbabilityOfCorrectOutcomePrediction int
}
// Map Structure: Trait Outcome Info -> Trait Prediction Accuracy Info
type TraitAccuracyInfoMap map[TraitOutcomeInfo]TraitPredictionAccuracyInfo
func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previousPage func()){
title := getBoldLabelCentered("Testing Model")
@ -1386,9 +1356,9 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
//Outputs:
// -bool: Process completed (true == was not stopped mid-way)
// -TraitAccuracyInfoMap
// -geneticPrediction.TraitPredictionAccuracyInfoMap
// -error
testModel := func()(bool, TraitAccuracyInfoMap, error){
testModel := func()(bool, geneticPrediction.TraitPredictionAccuracyInfoMap, error){
type TraitAccuracyStatisticsValue struct{
@ -1408,7 +1378,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
// We use this map to count up the information about predictions
// We use information from this map to construct the final accuracy information map
traitPredictionInfoMap := make(map[TraitOutcomeInfo]TraitAccuracyStatisticsValue)
traitPredictionInfoMap := make(map[geneticPrediction.TraitOutcomeInfo]TraitAccuracyStatisticsValue)
_, testingSetFilepathsList, err := getTrainingAndTestingDataFilepathLists(traitName)
@ -1494,7 +1464,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
{
// We first add the information to the map for the correct outcome
newTraitOutcomeInfo_CorrectOutcome := TraitOutcomeInfo{
newTraitOutcomeInfo_CorrectOutcome := geneticPrediction.TraitOutcomeInfo{
OutcomeName: correctOutcomeName,
PercentageOfLociTested: percentageOfLociTested,
@ -1525,7 +1495,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
{
// We now add the information to the map for the predicted outcome
newTraitOutcomeInfo_PredictedOutcome := TraitOutcomeInfo{
newTraitOutcomeInfo_PredictedOutcome := geneticPrediction.TraitOutcomeInfo{
OutcomeName: predictedOutcomeName,
PercentageOfLociTested: percentageOfLociTested,
@ -1566,7 +1536,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
// Now we construct the TraitAccuracyInfoMap
// This map stores the accuracy for each outcome
traitAccuracyInfoMap := make(map[TraitOutcomeInfo]TraitPredictionAccuracyInfo)
traitPredictionAccuracyInfoMap := make(map[geneticPrediction.TraitOutcomeInfo]geneticPrediction.TraitPredictionAccuracyInfo)
for traitAccuracyData, value := range traitPredictionInfoMap{
@ -1583,7 +1553,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
return false, nil, errors.New("traitPredictionInfoMap contains quantityOfCorrectOutcomePredictions > quantityOfPredictions")
}
newTraitPredictionAccuracyInfo := TraitPredictionAccuracyInfo{
newTraitPredictionAccuracyInfo := geneticPrediction.TraitPredictionAccuracyInfo{
QuantityOfExamples: quantityOfExamples,
QuantityOfPredictions: quantityOfPredictions,
}
@ -1604,17 +1574,30 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
newTraitPredictionAccuracyInfo.ProbabilityOfCorrectOutcomePrediction = percentageOfCorrectOutcomePredictions
}
traitAccuracyInfoMap[traitAccuracyData] = newTraitPredictionAccuracyInfo
traitPredictionAccuracyInfoMap[traitAccuracyData] = newTraitPredictionAccuracyInfo
}
// Testing is complete.
// We save the info map as a file in the ModelAccuracies folder
fileBytes, err := geneticPrediction.EncodeTraitPredictionAccuracyInfoMapToBytes(traitPredictionAccuracyInfoMap)
if (err != nil) { return false, nil, err }
_, err = localFilesystem.CreateFolder("./ModelAccuracies")
if (err != nil) { return false, nil, err }
modelAccuracyFilename := traitNameWithoutWhitespaces + "ModelAccuracy.gob"
err = localFilesystem.CreateOrOverwriteFile(fileBytes, "./ModelAccuracies/", modelAccuracyFilename)
if (err != nil) { return false, nil, err }
progressPercentageBinding.Set(1)
return true, traitAccuracyInfoMap, nil
return true, traitPredictionAccuracyInfoMap, nil
}
processIsComplete, traitAccuracyInfoMap, err := testModel()
processIsComplete, traitPredictionAccuracyInfoMap, err := testModel()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
@ -1624,14 +1607,14 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
return
}
setViewModelTestingTraitResultsPage(window, traitName, traitAccuracyInfoMap, previousPage)
setViewModelTestingTraitResultsPage(window, traitName, traitPredictionAccuracyInfoMap, previousPage)
}
go testModelFunction()
}
// This is a page to view the details of testing for a specific trait's model
func setViewModelTestingTraitResultsPage(window fyne.Window, traitName string, traitAccuracyInfoMap TraitAccuracyInfoMap, exitPage func()){
func setViewModelTestingTraitResultsPage(window fyne.Window, traitName string, traitAccuracyInfoMap geneticPrediction.TraitPredictionAccuracyInfoMap, exitPage func()){
title := getBoldLabelCentered("Trait Prediction Accuracy Details")
@ -1661,14 +1644,10 @@ func setViewModelTestingTraitResultsPage(window fyne.Window, traitName string, t
predictionAccuracyTitle3 := getItalicLabelCentered("Prediction Accuracy")
knownLociLabel_67to100 := getItalicLabelCentered("67-100% Known Loci")
emptyLabel2 := widget.NewLabel("")
emptyLabel3 := widget.NewLabel("")
outcomeNameColumn := container.NewVBox(outcomeNameTitle, emptyLabel1, widget.NewSeparator())
predictionAccuracyColumn_0to33 := container.NewVBox(predictionAccuracyTitle1, knownLociLabel_0to33, widget.NewSeparator())
predictionAccuracyColumn_34to66 := container.NewVBox(predictionAccuracyTitle2, knownLociLabel_34to66, widget.NewSeparator())
predictionAccuracyColumn_67to100 := container.NewVBox(predictionAccuracyTitle3, knownLociLabel_67to100, widget.NewSeparator())
viewTraitAccuracyDetailsColumn := container.NewVBox(emptyLabel2, emptyLabel3, widget.NewSeparator())
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return nil, err }
@ -1777,7 +1756,7 @@ func setViewModelTestingTraitResultsPage(window fyne.Window, traitName string, t
predictionAccuracyColumn_67to100.Add(widget.NewSeparator())
}
resultsGrid := container.NewHBox(layout.NewSpacer(), outcomeNameColumn, predictionAccuracyColumn_0to33, predictionAccuracyColumn_34to66, predictionAccuracyColumn_67to100, viewTraitAccuracyDetailsColumn, layout.NewSpacer())
resultsGrid := container.NewHBox(layout.NewSpacer(), outcomeNameColumn, predictionAccuracyColumn_0to33, predictionAccuracyColumn_34to66, predictionAccuracyColumn_67to100, layout.NewSpacer())
return resultsGrid, nil
}