Exercise 3 solution pedigree = { "Ivy": (None, None, "F"), "Joe": (None, None, "M"), "Kit": ("Joe", "Ivy", "M"), "Lia": ("Joe", "Ivy", "F"), "Max": (None, None, "M"), "Nell": ("Max", "Lia", "F"), "Otto": ("Max", "Lia", "M"), "Pia": ("Kit", "Nell", "F"), } def check(pedigree, affected): for name, (dad, mom, sx) in sorted(pedigree.items()): if sx == "M" and name in affected and dad in affected: return "does not fit simple X-linked recessive model: father " + dad + " to son " + name mothers = sorted({mom for name, (dad, mom, sx) in pedigree.items() if sx == "M" and name in affected and mom}) return "consistent; carrier mothers: " + ", ".join(mothers) print(check(pedigree, {"Kit", "Otto"})) print(check(pedigree, {"Joe", "Kit"})) print(check(pedigree, {"Pia"})) Output: consistent; carrier mothers: Ivy, Lia does not fit simple X-linked recessive model: father Joe to son Kit consistent; carrier mothers: Reading it: the first check looks for the one pattern that flags an affected father with an affected son, because a father gives his son a Y, never his X (a carrier mother could still explain it, so this is a warning sign, not proof). When there is none, every mother of an affected son must carry the allele. The second pedigree contains an affected father and son, so it is flagged. The third passes the check only because it has no affected sons: the check can rule a pattern out, never prove it.