2024-04-11 15:51:56 +02:00
|
|
|
|
|
|
|
// traits provides information about traits and the SNPs that influence them
|
|
|
|
|
|
|
|
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
|
|
|
|
//
|
2024-06-15 02:43:01 +02:00
|
|
|
// See geneticPrediction.go for a non-working attempt to predict traits with neural nets
|
2024-04-11 15:51:56 +02:00
|
|
|
|
|
|
|
import "errors"
|
|
|
|
|
|
|
|
type RuleLocus struct{
|
|
|
|
|
|
|
|
// 3 byte hex encoded string
|
|
|
|
LocusIdentifier 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
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type TraitRule struct{
|
|
|
|
|
|
|
|
// 3 byte identifier encoded hex
|
|
|
|
RuleIdentifier string
|
|
|
|
|
|
|
|
// A list of RuleLocus objects which comprise the rule
|
|
|
|
// The genome must have a required base pair for each locus in this list to pass the rule
|
|
|
|
LociList []RuleLocus
|
|
|
|
|
|
|
|
// The outcome that this rule will effect
|
|
|
|
// The number of points to add to the outcome if the rule RSID values are fulfilled
|
|
|
|
// Do not use negative values
|
|
|
|
|
|
|
|
// Map Structure: Outcome name -> Points to add to outcome if rule passes
|
|
|
|
OutcomePointsMap map[string]int
|
|
|
|
|
|
|
|
// Map structure: Reference name -> Reference link
|
|
|
|
References map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Trait struct{
|
|
|
|
|
|
|
|
TraitName string
|
|
|
|
|
|
|
|
TraitDescription 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 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
|
|
|
|
}
|
|
|
|
|
|
|
|
var traitNamesList []string
|
|
|
|
var traitObjectsList []Trait
|
|
|
|
|
|
|
|
// Map Structure: Rule locus identifier -> RSID representing this locus
|
|
|
|
var locusRSIDsMap map[string]int64
|
|
|
|
|
|
|
|
func InitializeTraitVariables(){
|
|
|
|
|
|
|
|
lactoseToleranceObject := getLactoseToleranceTraitObject()
|
|
|
|
hairTextureObject := getHairTextureTraitObject()
|
|
|
|
facialStructureObject := getFacialStructureTraitObject()
|
|
|
|
eyeColorObject := getEyeColorTraitObject()
|
|
|
|
hairColorObject := getHairColorTraitObject()
|
|
|
|
skinColorObject := getSkinColorTraitObject()
|
|
|
|
|
|
|
|
traitObjectsList = []Trait{lactoseToleranceObject, hairTextureObject, facialStructureObject, eyeColorObject, hairColorObject, skinColorObject}
|
|
|
|
|
|
|
|
traitNamesList = make([]string, 0, len(traitObjectsList))
|
|
|
|
locusRSIDsMap = make(map[string]int64)
|
|
|
|
|
|
|
|
for _, traitObject := range traitObjectsList{
|
|
|
|
|
|
|
|
traitName := traitObject.TraitName
|
|
|
|
|
|
|
|
traitNamesList = append(traitNamesList, traitName)
|
|
|
|
|
|
|
|
traitRulesList := traitObject.RulesList
|
|
|
|
|
|
|
|
for _, traitRuleObject := range traitRulesList{
|
|
|
|
|
|
|
|
ruleLociList := traitRuleObject.LociList
|
|
|
|
|
|
|
|
for _, ruleLocusObject := range ruleLociList{
|
|
|
|
|
|
|
|
locusIdentifier := ruleLocusObject.LocusIdentifier
|
|
|
|
locusRSID := ruleLocusObject.LocusRSID
|
|
|
|
|
|
|
|
locusRSIDsMap[locusIdentifier] = locusRSID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Be aware that all of these functions are returning original objects/slices, not copies
|
|
|
|
// Thus, we cannot edit the objects/slices that are returned. We must copy the fields first if we want to edit them.
|
|
|
|
|
|
|
|
func GetTraitNamesList()([]string, error){
|
|
|
|
|
|
|
|
if (traitNamesList == nil){
|
|
|
|
return nil, errors.New("GetTraitNamesList called when list is not initialized.")
|
|
|
|
}
|
|
|
|
|
|
|
|
return traitNamesList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetTraitObjectsList()([]Trait, error){
|
|
|
|
|
|
|
|
if (traitObjectsList == nil){
|
|
|
|
return nil, errors.New("GetTraitObjectsList called when list is not initialized.")
|
|
|
|
}
|
|
|
|
|
|
|
|
return traitObjectsList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetTraitObject(traitName string)(Trait, error){
|
|
|
|
|
|
|
|
traitObjectsList, err := GetTraitObjectsList()
|
|
|
|
if (err != nil) { return Trait{}, err }
|
|
|
|
|
|
|
|
for _, traitObject := range traitObjectsList{
|
|
|
|
|
|
|
|
currentTraitName := traitObject.TraitName
|
|
|
|
if (currentTraitName != traitName){
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return traitObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return Trait{}, errors.New("GetTraitObject called with unknown trait: " + traitName)
|
|
|
|
}
|
|
|
|
|
|
|
|
//Outputs:
|
|
|
|
// -map[string]TraitRule: Map of RuleIdentifier -> Rule Object
|
|
|
|
// -error (will return err if traitName is not found)
|
|
|
|
func GetTraitRulesMap(traitName string)(map[string]TraitRule, error){
|
|
|
|
|
|
|
|
traitObject, err := GetTraitObject(traitName)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
traitRulesList := traitObject.RulesList
|
|
|
|
|
|
|
|
traitRulesMap := make(map[string]TraitRule)
|
|
|
|
|
|
|
|
for _, ruleObject := range traitRulesList{
|
|
|
|
|
|
|
|
ruleIdentifier := ruleObject.RuleIdentifier
|
|
|
|
traitRulesMap[ruleIdentifier] = ruleObject
|
|
|
|
}
|
|
|
|
|
|
|
|
return traitRulesMap, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetTraitRuleObject(traitName string, ruleIdentifier string)(TraitRule, error){
|
|
|
|
|
|
|
|
traitRulesMap, err := GetTraitRulesMap(traitName)
|
|
|
|
if (err != nil){ return TraitRule{}, err }
|
|
|
|
|
|
|
|
ruleObject, exists := traitRulesMap[ruleIdentifier]
|
|
|
|
if (exists == false){
|
|
|
|
return TraitRule{}, errors.New("GetTraitRuleObject called with unknown ruleIdentifier: " + ruleIdentifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ruleObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//Outputs:
|
|
|
|
// -int64: The rsID which represents this locus
|
|
|
|
// -error
|
|
|
|
func GetTraitRuleLocusRSID(locusIdentifier string)(int64, error){
|
|
|
|
|
|
|
|
if (locusRSIDsMap == nil){
|
|
|
|
return 0, errors.New("GetTraitRuleLocusRSID called when locusRSIDsMap is not initialized.")
|
|
|
|
}
|
|
|
|
|
|
|
|
locusRSID, exists := locusRSIDsMap[locusIdentifier]
|
|
|
|
if (exists == false){
|
|
|
|
return 0, errors.New("GetTraitRuleLocusRSID called with unknown locusIdentifier: " + locusIdentifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
return locusRSID, nil
|
|
|
|
}
|
|
|
|
|