-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathsubstitution.cpp
548 lines (373 loc) · 19.3 KB
/
substitution.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
//
// substitution.cpp
// SLiM
//
// Created by Ben Haller on 12/13/14.
// Copyright (c) 2014-2025 Philipp Messer. All rights reserved.
// A product of the Messer Lab, http://messerlab.org/slim/
//
// This file is part of SLiM.
//
// SLiM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//
// SLiM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with SLiM. If not, see <http://www.gnu.org/licenses/>.
#include "substitution.h"
#include "slim_globals.h"
#include "eidos_call_signature.h"
#include "eidos_property_signature.h"
#include "species.h"
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#pragma mark -
#pragma mark Substitution
#pragma mark -
Substitution::Substitution(Mutation &p_mutation, slim_tick_t p_fixation_tick) :
EidosDictionaryRetained(), mutation_type_ptr_(p_mutation.mutation_type_ptr_), position_(p_mutation.position_), selection_coeff_(p_mutation.selection_coeff_), subpop_index_(p_mutation.subpop_index_), origin_tick_(p_mutation.origin_tick_), fixation_tick_(p_fixation_tick), chromosome_index_(p_mutation.chromosome_index_), nucleotide_(p_mutation.nucleotide_), mutation_id_(p_mutation.mutation_id_), tag_value_(p_mutation.tag_value_)
{
AddKeysAndValuesFrom(&p_mutation);
// No call to ContentsChanged() here; we know we use Dictionary not DataFrame, and Mutation already vetted the dictionary
}
Substitution::Substitution(slim_mutationid_t p_mutation_id, MutationType *p_mutation_type_ptr, slim_chromosome_index_t p_chromosome_index, slim_position_t p_position, double p_selection_coeff, slim_objectid_t p_subpop_index, slim_tick_t p_tick, slim_tick_t p_fixation_tick, int8_t p_nucleotide) :
mutation_type_ptr_(p_mutation_type_ptr), position_(p_position), selection_coeff_(static_cast<slim_selcoeff_t>(p_selection_coeff)), subpop_index_(p_subpop_index), origin_tick_(p_tick), fixation_tick_(p_fixation_tick), chromosome_index_(p_chromosome_index), nucleotide_(p_nucleotide), mutation_id_(p_mutation_id), tag_value_(SLIM_TAG_UNSET_VALUE)
{
}
void Substitution::PrintForSLiMOutput(std::ostream &p_out) const
{
p_out << mutation_id_ << " m" << mutation_type_ptr_->mutation_type_id_ << " " << position_;
// BCH 2/2/2025: Note that in multi-chrom models, this method now prints the chromosome symbol after the position
// For brevity and backward compatibility, the chromosome symbol is not printed in single-chromosome models
Species &species = mutation_type_ptr_->species_;
const std::vector<Chromosome *> &chromosomes = species.Chromosomes();
if (chromosomes.size() > 1)
{
Chromosome *chromosome = chromosomes[chromosome_index_];
p_out << " \"" << chromosome->Symbol() << "\"";
}
// and then the remainder of the output line
p_out << " " << selection_coeff_ << " " << mutation_type_ptr_->dominance_coeff_ << " p" << subpop_index_ << " " << origin_tick_ << " "<< fixation_tick_;
// output a nucleotide if available
if (mutation_type_ptr_->nucleotide_based_)
p_out << " " << gSLiM_Nucleotides[nucleotide_];
p_out << std::endl;
}
void Substitution::PrintForSLiMOutput_Tag(std::ostream &p_out) const
{
// BCH 4/7/2025: This is a copy of PrintForSLiMOutput() with output of tag_value_ added at the end
p_out << mutation_id_ << " m" << mutation_type_ptr_->mutation_type_id_ << " " << position_;
// BCH 2/2/2025: Note that in multi-chrom models, this method now prints the chromosome symbol after the position
// For brevity and backward compatibility, the chromosome symbol is not printed in single-chromosome models
Species &species = mutation_type_ptr_->species_;
const std::vector<Chromosome *> &chromosomes = species.Chromosomes();
if (chromosomes.size() > 1)
{
Chromosome *chromosome = chromosomes[chromosome_index_];
p_out << " \"" << chromosome->Symbol() << "\"";
}
// and then the remainder of the output line
p_out << " " << selection_coeff_ << " " << mutation_type_ptr_->dominance_coeff_ << " p" << subpop_index_ << " " << origin_tick_ << " "<< fixation_tick_;
// output a nucleotide if available
if (mutation_type_ptr_->nucleotide_based_)
p_out << " " << gSLiM_Nucleotides[nucleotide_];
// output the tag value, or '?' or the tag is not defined
if (tag_value_ == SLIM_TAG_UNSET_VALUE)
p_out << ' ' << '?';
else
p_out << ' ' << tag_value_;
p_out << std::endl;
}
//
// Eidos support
//
#pragma mark -
#pragma mark Eidos support
#pragma mark -
const EidosClass *Substitution::Class(void) const
{
return gSLiM_Substitution_Class;
}
void Substitution::Print(std::ostream &p_ostream) const
{
p_ostream << Class()->ClassNameForDisplay() << "<" << mutation_id_ << ":" << selection_coeff_ << ">";
}
EidosValue_SP Substitution::GetProperty(EidosGlobalStringID p_property_id)
{
// All of our strings are in the global registry, so we can require a successful lookup
switch (p_property_id)
{
// constants
case gID_chromosome:
{
Species &species = mutation_type_ptr_->species_;
const std::vector<Chromosome *> &chromosomes = species.Chromosomes();
Chromosome *chromosome = chromosomes[chromosome_index_];
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Object(chromosome, gSLiM_Chromosome_Class));
}
case gID_id: // ACCELERATED
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Int(mutation_id_));
case gID_mutationType: // ACCELERATED
return mutation_type_ptr_->SymbolTableEntry().second;
case gID_position: // ACCELERATED
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Int(position_));
case gID_selectionCoeff: // ACCELERATED
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Float(selection_coeff_));
case gID_originTick: // ACCELERATED
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Int(origin_tick_));
case gID_fixationTick: // ACCELERATED
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Int(fixation_tick_));
// variables
case gID_nucleotide: // ACCELERATED
{
if (nucleotide_ == -1)
EIDOS_TERMINATION << "ERROR (Substitution::GetProperty): property nucleotide is only defined for nucleotide-based mutations." << EidosTerminate();
switch (nucleotide_)
{
case 0: return gStaticEidosValue_StringA;
case 1: return gStaticEidosValue_StringC;
case 2: return gStaticEidosValue_StringG;
case 3: return gStaticEidosValue_StringT;
default:
EIDOS_TERMINATION << "ERROR (Substitution::GetProperty): (internal error) unrecognized value for nucleotide_." << EidosTerminate();
}
}
case gID_nucleotideValue: // ACCELERATED
{
if (nucleotide_ == -1)
EIDOS_TERMINATION << "ERROR (Substitution::GetProperty): property nucleotideValue is only defined for nucleotide-based mutations." << EidosTerminate();
switch (nucleotide_)
{
case 0: return gStaticEidosValue_Integer0;
case 1: return gStaticEidosValue_Integer1;
case 2: return gStaticEidosValue_Integer2;
case 3: return gStaticEidosValue_Integer3;
default:
EIDOS_TERMINATION << "ERROR (Substitution::GetProperty): (internal error) unrecognized value for nucleotide_." << EidosTerminate();
}
}
case gID_subpopID: // ACCELERATED
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Int(subpop_index_));
case gID_tag: // ACCELERATED
{
slim_usertag_t tag_value = tag_value_;
if (tag_value == SLIM_TAG_UNSET_VALUE)
EIDOS_TERMINATION << "ERROR (Substitution::GetProperty): property tag accessed on substitution before being set." << EidosTerminate();
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Int(tag_value));
}
// all others, including gID_none
default:
return super::GetProperty(p_property_id);
}
}
EidosValue *Substitution::GetProperty_Accelerated_id(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
int_result->set_int_no_check(value->mutation_id_, value_index);
}
return int_result;
}
EidosValue *Substitution::GetProperty_Accelerated_nucleotide(EidosObject **p_values, size_t p_values_size)
{
EidosValue_String *string_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_String())->Reserve((int)p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
int8_t nucleotide = value->nucleotide_;
if (nucleotide == -1)
EIDOS_TERMINATION << "ERROR (Substitution::GetProperty_Accelerated_nucleotide): property nucleotide is only defined for nucleotide-based mutations." << EidosTerminate();
if (nucleotide == 0)
string_result->PushString(gStr_A);
else if (nucleotide == 1)
string_result->PushString(gStr_C);
else if (nucleotide == 2)
string_result->PushString(gStr_G);
else if (nucleotide == 3)
string_result->PushString(gStr_T);
}
return string_result;
}
EidosValue *Substitution::GetProperty_Accelerated_nucleotideValue(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
int8_t nucleotide = value->nucleotide_;
if (nucleotide == -1)
EIDOS_TERMINATION << "ERROR (Substitution::GetProperty_Accelerated_nucleotideValue): property nucleotideValue is only defined for nucleotide-based mutations." << EidosTerminate();
int_result->set_int_no_check(nucleotide, value_index);
}
return int_result;
}
EidosValue *Substitution::GetProperty_Accelerated_originTick(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
int_result->set_int_no_check(value->origin_tick_, value_index);
}
return int_result;
}
EidosValue *Substitution::GetProperty_Accelerated_fixationTick(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
int_result->set_int_no_check(value->fixation_tick_, value_index);
}
return int_result;
}
EidosValue *Substitution::GetProperty_Accelerated_position(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
int_result->set_int_no_check(value->position_, value_index);
}
return int_result;
}
EidosValue *Substitution::GetProperty_Accelerated_subpopID(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
int_result->set_int_no_check(value->subpop_index_, value_index);
}
return int_result;
}
EidosValue *Substitution::GetProperty_Accelerated_tag(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Int *int_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Int())->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
slim_usertag_t tag_value = value->tag_value_;
if (tag_value == SLIM_TAG_UNSET_VALUE)
EIDOS_TERMINATION << "ERROR (Substitution::GetProperty_Accelerated_tag): property tag accessed on substitution before being set." << EidosTerminate();
int_result->set_int_no_check(tag_value, value_index);
}
return int_result;
}
EidosValue *Substitution::GetProperty_Accelerated_selectionCoeff(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Float *float_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Float())->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
float_result->set_float_no_check(value->selection_coeff_, value_index);
}
return float_result;
}
EidosValue *Substitution::GetProperty_Accelerated_mutationType(EidosObject **p_values, size_t p_values_size)
{
EidosValue_Object *object_result = (new (gEidosValuePool->AllocateChunk()) EidosValue_Object(gSLiM_MutationType_Class))->resize_no_initialize(p_values_size);
for (size_t value_index = 0; value_index < p_values_size; ++value_index)
{
Substitution *value = (Substitution *)(p_values[value_index]);
object_result->set_object_element_no_check_NORR(value->mutation_type_ptr_, value_index);
}
return object_result;
}
void Substitution::SetProperty(EidosGlobalStringID p_property_id, const EidosValue &p_value)
{
// All of our strings are in the global registry, so we can require a successful lookup
switch (p_property_id)
{
case gID_nucleotide:
{
const std::string &nucleotide = ((EidosValue_String &)p_value).StringRefAtIndex_NOCAST(0, nullptr);
if (nucleotide_ == -1)
EIDOS_TERMINATION << "ERROR (Substitution::SetProperty): property nucleotide is only defined for nucleotide-based substitutions." << EidosTerminate();
if (nucleotide == gStr_A) nucleotide_ = 0;
else if (nucleotide == gStr_C) nucleotide_ = 1;
else if (nucleotide == gStr_G) nucleotide_ = 2;
else if (nucleotide == gStr_T) nucleotide_ = 3;
else EIDOS_TERMINATION << "ERROR (Substitution::SetProperty): property nucleotide may only be set to 'A', 'C', 'G', or 'T'." << EidosTerminate();
return;
}
case gID_nucleotideValue:
{
int64_t nucleotide = p_value.IntAtIndex_NOCAST(0, nullptr);
if (nucleotide_ == -1)
EIDOS_TERMINATION << "ERROR (Substitution::SetProperty): property nucleotideValue is only defined for nucleotide-based substitutions." << EidosTerminate();
if ((nucleotide < 0) || (nucleotide > 3))
EIDOS_TERMINATION << "ERROR (Substitution::SetProperty): property nucleotideValue may only be set to 0 (A), 1 (C), 2 (G), or 3 (T)." << EidosTerminate();
nucleotide_ = (int8_t)nucleotide;
return;
}
case gID_subpopID:
{
slim_objectid_t value = SLiMCastToObjectidTypeOrRaise(p_value.IntAtIndex_NOCAST(0, nullptr));
subpop_index_ = value;
return;
}
case gID_tag:
{
slim_usertag_t value = SLiMCastToUsertagTypeOrRaise(p_value.IntAtIndex_NOCAST(0, nullptr));
tag_value_ = value;
return;
}
default:
{
return super::SetProperty(p_property_id, p_value);
}
}
}
EidosValue_SP Substitution::ExecuteInstanceMethod(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter)
{
switch (p_method_id)
{
default: return super::ExecuteInstanceMethod(p_method_id, p_arguments, p_interpreter);
}
}
//
// Substitution_Class
//
#pragma mark -
#pragma mark Substitution_Class
#pragma mark -
EidosClass *gSLiM_Substitution_Class = nullptr;
const std::vector<EidosPropertySignature_CSP> *Substitution_Class::Properties(void) const
{
static std::vector<EidosPropertySignature_CSP> *properties = nullptr;
if (!properties)
{
THREAD_SAFETY_IN_ANY_PARALLEL("Substitution_Class::Properties(): not warmed up");
properties = new std::vector<EidosPropertySignature_CSP>(*super::Properties());
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_chromosome, true, kEidosValueMaskObject | kEidosValueMaskSingleton, gSLiM_Chromosome_Class)));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_id, true, kEidosValueMaskInt | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_id));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_mutationType, true, kEidosValueMaskObject | kEidosValueMaskSingleton, gSLiM_MutationType_Class))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_mutationType));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_position, true, kEidosValueMaskInt | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_position));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_selectionCoeff, true, kEidosValueMaskFloat | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_selectionCoeff));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_subpopID, false, kEidosValueMaskInt | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_subpopID));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_nucleotide, false, kEidosValueMaskString | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_nucleotide));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_nucleotideValue, false, kEidosValueMaskInt | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_nucleotideValue));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_originTick, true, kEidosValueMaskInt | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_originTick));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_fixationTick, true, kEidosValueMaskInt | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_fixationTick));
properties->emplace_back((EidosPropertySignature *)(new EidosPropertySignature(gStr_tag, false, kEidosValueMaskInt | kEidosValueMaskSingleton))->DeclareAcceleratedGet(Substitution::GetProperty_Accelerated_tag));
std::sort(properties->begin(), properties->end(), CompareEidosPropertySignatures);
}
return properties;
}
const std::vector<EidosMethodSignature_CSP> *Substitution_Class::Methods(void) const
{
static std::vector<EidosMethodSignature_CSP> *methods = nullptr;
if (!methods)
{
THREAD_SAFETY_IN_ANY_PARALLEL("Substitution_Class::Methods(): not warmed up");
methods = new std::vector<EidosMethodSignature_CSP>(*super::Methods());
std::sort(methods->begin(), methods->end(), CompareEidosCallSignatures);
}
return methods;
}