seekia/resources/geneticReferences/traits/traits.go

209 lines
6 KiB
Go

// 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
//
// See geneticPrediction.go for a non-working attempt to predict traits with neural nets
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
}