Creating a list of fixed length to store objects type in the future

13 views
Skip to first unread message

Hanbin Lee

unread,
Oct 1, 2025, 1:54:13 PM (6 days ago) Oct 1
to slim-discuss
Hi, I'm trying to create a list of fixed length so that I can store objects in it (e.g. mutations).
The following is the code
"
initialize() {
defineConstant("lst", rep(NULL, 5));
print(length(lst));
}

It prints 0, no matter how long the list is.
I've tried it to replace NULL with a numeric type, but then the assignment with an object does not work.


Peter Ralph

unread,
Oct 1, 2025, 6:05:20 PM (6 days ago) Oct 1
to Hanbin Lee, slim-discuss
You can create "empty" lists of a given length for float, integer, logical, and string - e.g., string(5) - but from the help for object():
"
Returns a new empty object vector.  Unlike float(), integer(), logical(), and string(), a length cannot be specified and the new vector contains no elements.  This is because there is no default value for the object type.  Adding to such a vector is typically done with c().  Note that the return value is of type object<Object>; this method creates an object vector that does not know what element type it contains.  Such object vectors may be mixed freely with other object vectors in c() and similar contexts; the result of such mixing will take its object-element type from the object vector with a defined object-element type (if any).
"
So, it seems like if you need to do something like this, you need to define a 'placeholder' object to stand in for "nothing here"; how you do that probably depends on what the goal is.

-peter


--
SLiM forward genetic simulation: http://messerlab.org/slim/
Before posting, please read http://benhaller.com/slim/bugs_and_questions.html
---
You received this message because you are subscribed to the Google Groups "slim-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to slim-discuss...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/slim-discuss/3b304860-f9bc-4c72-b9ad-136ee52c2967n%40googlegroups.com.

Hanbin Lee

unread,
Oct 1, 2025, 6:47:34 PM (6 days ago) Oct 1
to slim-discuss
I intended to save mutations inside that vector (which I think is the correct Eidos term), but couldn't figure out how to create a mutation object properly. Following are some of the candidates I've tried so far, which I think aren't *nice*. Any other suggestions?

Case 1: (when using an existing mutation)
```
muts = sim.mutationsOfType(m1);
mutPlaceHolder = muts[0];
defineConstant("lst", rep(mutPlaceHolder, 5));
```
Case 2: (when no mutation exists - adaptation of SLiM manual recepie)
```
target = p1.haplosomes[0];
target.addNewDrawnMutation(m1, 0);
muts = target.mutations();
mutPlaceHolder = muts[0];
defineConstant("lst", rep(mutPlaceHolder, 5));
target.removeMutations(muts);
```



Ben Haller

unread,
Oct 1, 2025, 7:13:45 PM (6 days ago) Oct 1
to Hanbin Lee, slim-discuss
Hi!  Yes, Peter had the right end of the stick; Eidos has no way to represent "lack of an object" the way that nullptr in C/C++ does, or that NA in R does.  There is no value that you can mix together with mutation objects in a vector represent the lack of a mutation object at a given position.  NULL doesn't work because it is not like NA; it is its own data type, and data types cannot be mixed within a single vector in Eidos.  And c(NULL, NULL) is NULL, so you can't make a vector out of NULL either; size(NULL) is zero.

That said, there are a variety of ways that you could approach this, starting with the ones you've explored:

1. Use an existing mutation as a placeholder; far from ideal since you can't tell the difference between "no mutation object" and the particular mutation object you've arbitrarily chosen as a placeholder.

2. Make a custom mutation as a placeholder, otherwise unused in your model; the is somewhat better, but ugly.

3. Make a Dictionary, using integers as keys, with NULL in the keys 0-4; you are then free to replace the value for any key with a mutation object.  This is not bad, I think, but is probably not terribly efficient.  Whether that matters depends, of course, on what you're really trying to achieve, which you haven't told us, and which might be helpful to know.  :->  Dictionary substitutes for a lot of data structures in other languages; since Eidos provides no way to define new types/structs/classes; if you want "a bag of information that behaves in a particular way", doing it with Dictionary is often the best path in Eidos.

4. Keep a vector of mutation IDs, rather than mutations, and use -1 as a placeholder for "no mutation".  You could use subsetMutations() to look up any given ID (provided it is not -1), or you could use match() with a bit of elbow grease to look up the mutations that match a whole vector of IDs.  The lookup part of this solution is a bit annoying, but the rest is straightforward.

5. Keep a separate variable for each index: mut0, mut1, mut2, mut3, mut4.  Initialize them all to NULL initially, and then you can individually set/get the values, although "subsetting" with an integer index would be annoying and inefficient.  Depending on the nature of the problem, this might work just fine, or not.

6. Come up with a different approach to your problem (whatever it is) that doesn't require this particular data structure.  This is probably the easiest, if it is possible.  We could try to help with that, if you told us what the actual problem is.  :->

Good luck!

Cheers,
-B.


Hanbin Lee wrote on 10/1/25 6:47 PM:

Hanbin Lee

unread,
Oct 1, 2025, 7:47:59 PM (6 days ago) Oct 1
to slim-discuss
This is my attempt to simulate a biallelic locus with initial burn-in by msprime. Thanks to Peter and Ben for all the assistance. 
It currently works only with one locus but it should be straightforward to extend.

Python
```
num_individuals = 100
mu, sequence_length = 1 / num_individuals, 1
ts = msprime.sim_ancestry(num_individuals, sequence_length=sequence_length, population_size=num_individuals, random_seed=123)
ts = msprime.sim_mutations(ts, rate=mu, model=msprime.BinaryMutationModel(), random_seed=123)
assert ts.num_mutations > 1

t = ts.dump_tables() # obtain tables from ts
pyslim.annotate_tables(t, model_type="WF", tick=1) # annotate tables for slim
ts_pre = t.tree_sequence()
muts = t.mutations.copy() # copy mutations (will be modified)
t.mutations.clear() # clean mutation table w/o any mutations

for m, tm in zip(ts.mutations(), muts):
    ds, md = tm.derived_state, tm.metadata
    if m.derived_state == "0": # derived state 0 in original tree sequence
        ds, md = "", {"mutation_list": []} # derived state 0 means wildtype
    else:
        ds = "0" # should change when seq_length > 1
        md["mutation_list"][0]["mutation_type"] = 1
    t.mutations.append(tm.replace(derived_state=ds, metadata=md)) # update table mutation

ts_post = t.tree_sequence()

in_path = "msprime_annotated.tree"
ts_post.dump(in_path)
```
The SLiM code (biallelic_mod.slim)
```
initialize() {
initializeTreeSeq();

initializeMutationType("m1", 0.5, "f", 0.0);
m1.convertToSubstitution = F;

initializeMutationType("m0", 0.5, "f", 0.0);
m0.convertToSubstitution = F;

initializeGenomicElementType("g1", m0, 1.0);
initializeGenomicElement(g1, 0, sequenceLength-1);
initializeMutationRate(mu);
initializeRecombinationRate(0);

}

1 early() {
    sim.readFromPopulationFile(inTreeSequence);

    muts = sim.mutationsOfType(m1);
    mutPlaceHolder = muts[0];
    defineGlobal("MUT", rep(mutPlaceHolder, sequenceLength));
    defineGlobal("MUT_EXIST", rep(0, sequenceLength));
for (mut in sim.mutationsOfType(m1)) {
MUT[mut.position] = mut;
        MUT_EXIST[mut.position] = 1;
}
   
}

mutation(m0) {
    if (haplosome.containsMarkerMutation(m1, mut.position)) {
        return T;
    }    
    if (MUT_EXIST[mut.position] == 1) {
        return MUT[mut.position];
    } else {
        mut.setMutationType(m1);
MUT[mut.position] = mut;
        MUT_EXIST[mut.position] = 1;
        return MUT[mut.position];
    }
}
   
late() {
m0muts = sim.mutationsOfType(m0);
if (m0muts.length() > 0) {
haplosomes = sim.subpopulations.haplosomes;
counts = haplosomes.countOfMutationsOfType(m0);
hasStacked = haplosomes[counts > 0];
for (haplosome in hasStacked) {
stacked_m0 = haplosome.mutationsOfType(m0);
stackPositions = stacked_m0.position;
all_m1 = haplosome.mutationsOfType(m1);
s = (match(all_m1.position, stackPositions) >= 0);
stacked_m1 = all_m1[s];
haplosome.removeMutations(c(stacked_m0, stacked_m1));
}
}
}

100 late() {
log = community.createLogFile("freq.csv", logInterval=10);
log.addTick();
log.addMeanSDColumns("freq", "sim.mutationFrequencies(NULL, sim.mutationsOfType(m1));");
sim.treeSeqOutput(outTreeSequence);
}
```
Finally, the execution in Python via subprocess
```
out_path = "slim_mod_ts.tree"
in_path = "msprime_annotated.tree"
slim_args = [
        "conda", "run", "-n", "tskit_dev",
        "slim",
        "-d", f"sequenceLength={sequence_length}",
        "-d", f"numIndividuals={num_individuals}",
        "-d", f"mu={mu}",
        "-d", f"outTreeSequence='{out_path}'",
        "-d", f"inTreeSequence='{in_path}'",
        #"-s", "1234",
        "test/biallelic_mod.slim"
]
result = subprocess.run(slim_args)
```
Reply all
Reply to author
Forward
0 new messages