// 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[Reference Name]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 function returns a formatted, translated representation of a numeric value for this trait // For example, "150 centimeters", "5 foot 10 inches", "4/10" // Inputs: // -float64: The value to format // -bool: Show "/10" units // -The "/10" units are used for certain traits, where the unit value is not a real-world measurement (such as length, weight, etc.) // -An example of an "/10" trait is Homosexuality, which is represented by a value between 0 and 10. // -We don't want to "/10" if we are displaying a confidence range (Example: +/- 5.2) // -If we did, then people would get confused by thinking the unit represents a fraction NumericValueFormatter func(float64, bool)(string, error) // 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() homosexualnessObject := getHomosexualnessTraitObject() heightObject, err := getHeightTraitObject() if (err != nil){ return err } traitObjectsList = []Trait{lactoseToleranceObject, hairTextureObject, facialStructureObject, eyeColorObject, hairColorObject, skinColorObject, heightObject, homosexualnessObject} 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 }