225 lines
6.5 KiB
Go
225 lines
6.5 KiB
Go
|
|
// traits provides information about traits and the SNPs that influence them
|
|
|
|
package traits
|
|
|
|
import "errors"
|
|
|
|
type Trait struct{
|
|
|
|
// Example: "Eye Color"
|
|
TraitName string
|
|
|
|
TraitDescription 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
|
|
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
|
|
ReferencesMap map[string]string
|
|
}
|
|
|
|
|
|
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
|
|
}
|
|
|
|
|
|
var traitNamesList []string
|
|
var traitObjectsList []Trait
|
|
|
|
// Map Structure: Rule locus identifier -> RSID representing this locus
|
|
var locusRSIDsMap map[string]int64
|
|
|
|
func InitializeTraitVariables()error{
|
|
|
|
lactoseToleranceObject := getLactoseToleranceTraitObject()
|
|
hairTextureObject := getHairTextureTraitObject()
|
|
facialStructureObject := getFacialStructureTraitObject()
|
|
eyeColorObject := getEyeColorTraitObject()
|
|
hairColorObject := getHairColorTraitObject()
|
|
skinColorObject := getSkinColorTraitObject()
|
|
|
|
heightObject, err := getHeightTraitObject()
|
|
if (err != nil){ return err }
|
|
|
|
traitObjectsList = []Trait{lactoseToleranceObject, hairTextureObject, facialStructureObject, eyeColorObject, hairColorObject, skinColorObject, heightObject}
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|