Improved the genetic analysis creation process in various ways. Recombination breakpoints are more accurately predicted now.

This commit is contained in:
Simon Sarasova 2024-06-05 04:10:35 +00:00
parent 497f596b3b
commit ee976f49b3
No known key found for this signature in database
GPG key ID: EEDA4103C9C36944
5 changed files with 874 additions and 587 deletions

View file

@ -6,6 +6,7 @@ Small and insignificant changes may not be included in this log.
## Unversioned Changes
* Improved the genetic analysis creation process in various ways. Recombination breakpoints are more accurately predicted now. - *Simon Sarasova*
* Improved the identity hash generation tool. The fastest quantity of goroutines is now identified and used. - *Simon Sarasova*
* Improved the creation procedures, encoding format, and graphical presentation of genetic analyses. Map lists have been replaced by custom objects. - *Simon Sarasova*
* Upgraded Circl to version 1.3.8. - *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 | 249
Simon Sarasova | June 13, 2023 | 250

View file

@ -20,6 +20,7 @@ import "seekia/internal/appMemory"
import "seekia/internal/encoding"
import "seekia/internal/genetics/companyAnalysis"
import "seekia/internal/genetics/createGeneticAnalysis"
import "seekia/internal/genetics/locusValue"
import "seekia/internal/genetics/myChosenAnalysis"
import "seekia/internal/genetics/myPeople"
import "seekia/internal/genetics/readGeneticAnalysis"
@ -3349,7 +3350,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
}
userLocusBase1, userLocusBase2, semicolonExists := strings.Cut(userLocusBasePair, ";")
if (semicolonExists == true){
if (semicolonExists == false){
return false, 0, false, "", errors.New("Database corrupt: Contains profile with invalid " + locusName + " value: " + userLocusBasePair)
}
@ -3659,94 +3660,46 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring st
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, nil, 0, err }
offspringTraitOutcomeScoresMap := make(map[string]float64)
offspringNumberOfRulesTested := 0
// We construct the user's trait locus values map
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, traitRuleObject := range traitRulesList{
traitLociList := traitObject.LociList
ruleLociList := traitRuleObject.LociList
for _, rsID := range traitLociList{
//Outputs:
// -bool: Probability is known
// -float64: Offspring probability of passing rule
// -error
getOffspringProbabilityOfPassingRule := func()(bool, float64, error){
rsIDString := helpers.ConvertInt64ToString(rsID)
offspringProbabilityOfPassingRule := float64(1)
userLocusValueAttributeName := "LocusValue_rs" + rsIDString
for _, ruleLocusObject := range ruleLociList{
locusRSID := ruleLocusObject.LocusRSID
locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
userLocusValueAttributeName := "LocusValue_rs" + locusRSIDString
userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
if (err != nil) { return false, 0, err }
if (userLocusBasePairIsKnown == false){
// We must know all rule loci base pairs to determine offspring probability of passing rule
return false, 0, nil
}
userLocusBase1, userLocusBase2, semicolonExists := strings.Cut(userLocusBasePair, ";")
if (semicolonExists == false){
return false, 0, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + ": " + userLocusBasePair)
}
myLocusValue, myLocusValueIsKnown := myTraitLocusValuesMap[locusRSID]
if (myLocusValueIsKnown == false){
// We must know all rule loci base pairs to determine offspring probability of passing rule
return false, 0, nil
}
myLocusBase1 := myLocusValue.Base1Value
myLocusBase2 := myLocusValue.Base2Value
locusRequiredBasePairsList := ruleLocusObject.BasePairsList
offspringProbabilityOfPassingRuleLocus, err := createGeneticAnalysis.GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, userLocusBase1, userLocusBase2, myLocusBase1, myLocusBase2)
if (err != nil) { return false, 0, err }
offspringProbabilityOfPassingRule *= offspringProbabilityOfPassingRuleLocus
}
return true, offspringProbabilityOfPassingRule, nil
}
offspringProbabilityOfPassingRuleKnown, offspringProbabilityOfPassingRule, err := getOffspringProbabilityOfPassingRule()
userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
if (err != nil) { return false, nil, 0, err }
if (offspringProbabilityOfPassingRuleKnown == false){
if (userLocusBasePairIsKnown == false){
continue
}
offspringNumberOfRulesTested += 1
ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap
for traitOutcome, pointsEffect := range ruleOutcomePointsMap{
pointsToAdd := float64(pointsEffect) * offspringProbabilityOfPassingRule
offspringTraitOutcomeScoresMap[traitOutcome] += pointsToAdd
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";")
if (semicolonFound == false){
return false, nil, 0, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + " value: " + userLocusBasePair)
}
userLocusValue := locusValue.LocusValue{
Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
//TODO: Share LocusIsPhased information in user profiles and retrieve it into this value
LocusIsPhased: false,
}
userTraitLocusValuesMap[rsID] = userLocusValue
}
if (offspringNumberOfRulesTested == 0){
anyRuleTested, offspringNumberOfRulesTested, _, offspringAverageOutcomeScoresMap, err := createGeneticAnalysis.GetOffspringTraitInfo(traitObject, myTraitLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, nil, 0, err }
if (anyRuleTested == false){
return false, nil, 0, nil
}
traitOutcomesList := traitObject.OutcomesList
// We add all outcomes for which there were no points
for _, traitOutcome := range traitOutcomesList{
_, exists := offspringTraitOutcomeScoresMap[traitOutcome]
if (exists == false){
offspringTraitOutcomeScoresMap[traitOutcome] = 0
}
}
return true, offspringTraitOutcomeScoresMap, offspringNumberOfRulesTested, nil
return true, offspringAverageOutcomeScoresMap, offspringNumberOfRulesTested, nil
}
if (userOrOffspring == "User"){
@ -3903,11 +3856,103 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, viewTraitInfoButton, layout.NewSpacer())
//Outputs:
// -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){
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return false, nil, err }
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// Without my genome person chosen, all offspring rule probabilities are unknown
return false, nil, nil
}
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, nil, err }
if (len(myTraitLocusValuesMap) == 0){
return false, nil, nil
}
return true, myTraitLocusValuesMap, nil
}
anyMyTraitLocusValuesExist, myTraitLocusValuesMap, err := getMyTraitLocusValuesMap()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
traitLociList := traitObject.LociList
traitRulesList := traitObject.RulesList
//Outputs:
// -bool: Any trait locus value exists for this user
// -map[int64]locusValue.LocusValue: User locus values map
// -error
getUserTraitLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
// We construct the user's trait locus values map
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, rsID := range traitLociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
userLocusValueAttributeName := "LocusValue_rs" + rsIDString
userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
if (err != nil) { return false, nil, err }
if (userLocusBasePairIsKnown == false){
continue
}
userLocusBase1, userLocusBase2, semicolonFound := strings.Cut(userLocusBasePair, ";")
if (semicolonFound == false){
return false, nil, errors.New("Database corrupt: Contains profile with invalid " + userLocusValueAttributeName + " value: " + userLocusBasePair)
}
userLocusValue := locusValue.LocusValue{
Base1Value: userLocusBase1,
Base2Value: userLocusBase2,
//TODO: Share LocusIsPhased information in user profiles and retrieve it into this value
LocusIsPhased: false,
}
userTraitLocusValuesMap[rsID] = userLocusValue
}
if (len(userTraitLocusValuesMap) == 0){
return false, nil, nil
}
return true, userTraitLocusValuesMap, nil
}
anyUserTraitLocusValueExists, userTraitLocusValuesMap, err := getUserTraitLocusValuesMap()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
//Outputs:
// -bool: Status is known
// -bool: User passes rule
// -error
getUserPassesRuleBool := func(ruleIdentifier string, ruleLociList []traits.RuleLocus)(bool, bool, error){
getUserPassesRuleBool := func(ruleLociList []traits.RuleLocus)(bool, bool, error){
if (anyUserTraitLocusValueExists == false){
return false, false, nil
}
allRuleLociKnown := true
@ -3915,19 +3960,19 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
locusRSID := ruleLocusObject.LocusRSID
locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
userLocusValueAttributeName := "LocusValue_rs" + locusRSIDString
userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
if (err != nil) { return false, false, err }
if (userLocusBasePairIsKnown == false){
userLocusValue, userLocusValueIsKnown := userTraitLocusValuesMap[locusRSID]
if (userLocusValueIsKnown == false){
// We know rule is not passed
// We keep searching to see if ruleIsPassed status is No or Unknown
allRuleLociKnown = false
continue
continue
}
userLocusBase1Value := userLocusValue.Base1Value
userLocusBase2Value := userLocusValue.Base2Value
userLocusBasePair := userLocusBase1Value + ";" + userLocusBase2Value
ruleLocusBasePairsList := ruleLocusObject.BasePairsList
userPassesRuleLocus := slices.Contains(ruleLocusBasePairsList, userLocusBasePair)
@ -3943,103 +3988,54 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
return true, true, nil
}
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
//Outputs:
// -bool: Any offspring probability of passing rule is known
// -map[[3]byte]int: Offspring probability of passing rules map
// Map Structure: Rule Identifier -> Probability offspring will pass rule (0-100%)
// -error
getOffspringProbabilityOfPassingRulesMap := func()(bool, map[[3]byte]int, error){
if (anyMyTraitLocusValuesExist == false || anyUserTraitLocusValueExists == false){
return false, nil, nil
}
anyOffspringRulesTested, _, offspringProbabilityOfPassingRulesMap, _, err := createGeneticAnalysis.GetOffspringTraitInfo(traitObject, myTraitLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, nil, err }
if (anyOffspringRulesTested == false){
return false, nil, nil
}
return true, offspringProbabilityOfPassingRulesMap, nil
}
anyOffspringProbabilityOfPassingRuleIsKnown, offspringProbabilityOfPassingRulesMap, err := getOffspringProbabilityOfPassingRulesMap()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
//Outputs:
// -bool: Probability is known
// -int: Probability of passing rule (0-100)
// -error
getOffspringProbabilityOfPassingRule := func(ruleIdentifier string, ruleLociList []traits.RuleLocus)(bool, int, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// Without my genome person chosen, all offspring rule probabilities are unknown
return false, 0, nil
}
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, 0, err }
offspringProbabilityOfPassingRule := float64(1)
for _, ruleLocusObject := range ruleLociList{
locusRSID := ruleLocusObject.LocusRSID
locusRSIDString := helpers.ConvertInt64ToString(locusRSID)
userLocusValueAttributeName := "LocusValue_rs" + locusRSIDString
userLocusBasePairIsKnown, _, userLocusBasePair, err := getAnyUserProfileAttributeFunction(userLocusValueAttributeName)
if (err != nil) { return false, 0, err }
if (userLocusBasePairIsKnown == false){
// We must know all rule loci base pairs to determine offspring probability of passing rule
return false, 0, nil
}
userLocusBase1, userLocusBase2, semicolonExists := strings.Cut(userLocusBasePair, ";")
if (semicolonExists == false){
return false, 0, errors.New("Database corrupt: Contains profile with invalid: " + userLocusValueAttributeName + ": " + userLocusBasePair)
}
myLocusValue, myLocusValueIsKnown := myTraitLocusValuesMap[locusRSID]
if (myLocusValueIsKnown == false){
// We must know all rule loci base pairs to determine offspring probability of passing rule
return false, 0, nil
}
myLocusBase1 := myLocusValue.Base1Value
myLocusBase2 := myLocusValue.Base2Value
locusRequiredBasePairsList := ruleLocusObject.BasePairsList
offspringProbabilityOfPassingRuleLocus, err := createGeneticAnalysis.GetOffspringTraitRuleLocusInfo(locusRequiredBasePairsList, userLocusBase1, userLocusBase2, myLocusBase1, myLocusBase2)
if (err != nil) { return false, 0, err }
offspringProbabilityOfPassingRule *= offspringProbabilityOfPassingRuleLocus
}
offspringPercentageProbabilityOfPassingRule := int(offspringProbabilityOfPassingRule * 100)
return true, offspringPercentageProbabilityOfPassingRule, nil
}
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
traitRulesList := traitObject.RulesList
totalNumberOfTraitRules := len(traitRulesList)
getNumberOfRulesTested := func()(int, error){
if (userOrOffspring == "Offspring"){
if (anyOffspringProbabilityOfPassingRuleIsKnown == false){
return 0, nil
}
numberOfRulesTested := len(offspringProbabilityOfPassingRulesMap)
return numberOfRulesTested, nil
}
numberOfRulesTested := 0
for _, ruleObject := range traitRulesList{
ruleIdentifier := ruleObject.RuleIdentifier
ruleLociList := ruleObject.LociList
if (userOrOffspring == "User"){
ruleStatusIsKnown, _, err := getUserPassesRuleBool(ruleIdentifier, ruleLociList)
if (err != nil) { return 0, err }
if (ruleStatusIsKnown == true){
numberOfRulesTested += 1
}
} else if (userOrOffspring == "Offspring"){
ruleProbabilityIsKnown, _, err := getOffspringProbabilityOfPassingRule(ruleIdentifier, ruleLociList)
if (err != nil) { return 0, err }
if (ruleProbabilityIsKnown == true){
numberOfRulesTested += 1
}
ruleStatusIsKnown, _, err := getUserPassesRuleBool(ruleLociList)
if (err != nil) { return 0, err }
if (ruleStatusIsKnown == true){
numberOfRulesTested += 1
}
}
@ -4054,6 +4050,7 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
rulesTestedLabel := widget.NewLabel("Rules Tested:")
totalNumberOfTraitRules := len(traitRulesList)
numberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested)
totalNumberOfTraitRulesString := helpers.ConvertIntToString(totalNumberOfTraitRules)
@ -4119,12 +4116,16 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
for _, ruleObject := range traitRulesList{
ruleIdentifier := ruleObject.RuleIdentifier
ruleIdentifierHex := ruleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return nil, err }
ruleLociList := ruleObject.LociList
getUserPassesRuleString := func()(string, error){
userRuleStatusIsKnown, userPassesRule, err := getUserPassesRuleBool(ruleIdentifier, ruleLociList)
userRuleStatusIsKnown, userPassesRule, err := getUserPassesRuleBool(ruleLociList)
if (err != nil) { return "", err }
if (userRuleStatusIsKnown == false){
@ -4141,12 +4142,17 @@ func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, use
getOffspringProbabilityOfPassingRuleString := func()(string, error){
probabilityIsKnown, probabilityOfPassingRule, err := getOffspringProbabilityOfPassingRule(ruleIdentifier, ruleLociList)
if (err != nil) { return "", err }
if (anyOffspringProbabilityOfPassingRuleIsKnown == false){
result := translate("Unknown")
return result, nil
}
probabilityOfPassingRule, probabilityIsKnown := offspringProbabilityOfPassingRulesMap[ruleIdentifier]
if (probabilityIsKnown == false){
result := translate("Unknown")
return result, nil
}
ruleProbabilityString := helpers.ConvertIntToString(probabilityOfPassingRule)
ruleProbabilityFormatted := ruleProbabilityString + "%"
@ -4160,9 +4166,9 @@ 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, ruleIdentifier, currentPage)
setViewTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
})
ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifier)
ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifierHex)
userPassesRuleLabel := getBoldLabelCentered(userPassesRuleString)
offspringProbabilityOfPassingRuleLabel := getBoldLabelCentered(offspringProbabilityOfPassingRuleString)

View file

@ -320,72 +320,75 @@ func GetLocusMetadataObjectsListByChromosome(chromosome int)([]LocusMetadata, er
// -[]byte: File bytes
getFileBytes := func()(bool, []byte){
if (chromosome == 1){
return true, LocusMetadataFile_Chromosome1
}
if (chromosome == 2){
return true, LocusMetadataFile_Chromosome2
}
if (chromosome == 3){
return true, LocusMetadataFile_Chromosome3
}
if (chromosome == 4){
return true, LocusMetadataFile_Chromosome4
}
if (chromosome == 5){
return true, LocusMetadataFile_Chromosome5
}
if (chromosome == 6){
return true, LocusMetadataFile_Chromosome6
}
if (chromosome == 7){
return true, LocusMetadataFile_Chromosome7
}
if (chromosome == 8){
return true, LocusMetadataFile_Chromosome8
}
if (chromosome == 9){
return true, LocusMetadataFile_Chromosome9
}
if (chromosome == 10){
return true, LocusMetadataFile_Chromosome10
}
if (chromosome == 11){
return true, LocusMetadataFile_Chromosome11
}
if (chromosome == 12){
return true, LocusMetadataFile_Chromosome12
}
if (chromosome == 13){
return true, LocusMetadataFile_Chromosome13
}
if (chromosome == 14){
return true, LocusMetadataFile_Chromosome14
}
if (chromosome == 15){
return true, LocusMetadataFile_Chromosome15
}
if (chromosome == 16){
return true, LocusMetadataFile_Chromosome16
}
if (chromosome == 17){
return true, LocusMetadataFile_Chromosome17
}
//if (chromosome == 18){
// return true, LocusMetadataFile_Chromosome18
//}
if (chromosome == 19){
return true, LocusMetadataFile_Chromosome19
}
if (chromosome == 20){
return true, LocusMetadataFile_Chromosome20
}
if (chromosome == 21){
return true, LocusMetadataFile_Chromosome21
}
if (chromosome == 22){
return true, LocusMetadataFile_Chromosome22
switch chromosome{
case 1:{
return true, LocusMetadataFile_Chromosome1
}
case 2:{
return true, LocusMetadataFile_Chromosome2
}
case 3:{
return true, LocusMetadataFile_Chromosome3
}
case 4:{
return true, LocusMetadataFile_Chromosome4
}
case 5:{
return true, LocusMetadataFile_Chromosome5
}
case 6:{
return true, LocusMetadataFile_Chromosome6
}
case 7:{
return true, LocusMetadataFile_Chromosome7
}
case 8:{
return true, LocusMetadataFile_Chromosome8
}
case 9:{
return true, LocusMetadataFile_Chromosome9
}
case 10:{
return true, LocusMetadataFile_Chromosome10
}
case 11:{
return true, LocusMetadataFile_Chromosome11
}
case 12:{
return true, LocusMetadataFile_Chromosome12
}
case 13:{
return true, LocusMetadataFile_Chromosome13
}
case 14:{
return true, LocusMetadataFile_Chromosome14
}
case 15:{
return true, LocusMetadataFile_Chromosome15
}
case 16:{
return true, LocusMetadataFile_Chromosome16
}
case 17:{
return true, LocusMetadataFile_Chromosome17
}
//case 18:{
// return true, LocusMetadataFile_Chromosome18
//}
case 19:{
return true, LocusMetadataFile_Chromosome19
}
case 20:{
return true, LocusMetadataFile_Chromosome20
}
case 21:{
return true, LocusMetadataFile_Chromosome21
}
case 22:{
return true, LocusMetadataFile_Chromosome22
}
}
return false, nil
}