MATLAB Answers

deleting or removing variant using variant array handle gives unexpected results.

5 views (last 30 days)
If I have a variable that contains a SimBiology parameter object, and I change it, the object reflects that change:
par1 = addparameter(rx1kl,'kf');
>> par1.Value = 0.3
par1 =
struct with fields:
Value: 0.3000
This is expected behavior. If I change an object, I should see that change reflected in the object.
This does not happen with variant arrays. If i create a model mc with several variants, I can a Variant Array object, thus
mcv = mc.Variants;
Supposing I want to delete the second variant in the array, which is named 'Var2'. I should be able to do this in a few ways, with the variant array object.
% Either
delete(mcv(2)); % Method 1, or
removevariant(mc,mcv(2)) % Method 2, or
removevariant(mc,'Var2') % Method 3
However, if afterwards I look at the mcv object, the array is the same size as before the deletion. And Method 1 changes the mcv object from a Simbiology Variant Object to a modified array using handles. That is, if I display the object using
I get different results than if I refresh the mcv variable:
mcv = mc.Variant
the variant is seen to have been deleted from the model object, but not from the variant object denoted mcv.
So in one case (parameters) modifying the object shows up as expected. In another case(variant arrays) certain modifications do not have the expected outcome and modify the named object ('mcv') in different and unexpected ways. And given that the mcv and mc.Variant objects are different after the delete() and remove() functions I suspect that creating mcv creates a handle to a copy of the object. But the model object itself appears to have been modifed as expected. I don't understand it, anyway.
The workaround is to do an mcv=mc.Variant command after every delete() or removevariant() command. I suspect this is not as intended. What's going on with this? Or am I doing something wrong?
I've created a script to show the behavior
%% Demo weird variant behavior
dm = createnewmodel; % See function below
dcv = dm.Variants; % create a handle for the variant array
disp('Result of removing variant 2 with removevariant() function')
disp('Original Array, using handle:');
dcv % display original variant array
disp('Modified Array, using handle:');
dcv % display original variant array
disp('Not deleted in handle!');
disp('Showing that this removal affects the actual model');
dcv = dm.Variants % display variant array
disp('Result of removing variant 2 with delete() function')
dm2 = createnewmodel;
dcv2 = dm2.Variants;
disp('Another model, original variant array:');
delete(dcv2(2)); % use delete to delete variant per documentation
disp('Modified Array, using handle:');
disp('Supposedly deleted variant');
disp('Showing effect on model')
dcv2=dm2.Variants % only shows array of handles
function dm = createnewmodel();
dm = sbiomodel('demomodel'); % demo model
dc = addcompartment(dm,'democomp'); % add one compartment
dc.Capacity = 5;
dc.CapacityUnits = 'liter';
ds1 = addspecies(dc,'dspec1'); % add two species
ds2 = addspecies(dc,'dspec2');
set(ds1,'InitialAmount',100); % set ICs
rx1 = addreaction(dm,'dspec1','dspec2'); % add one reaction
rx1kl = addkineticlaw(rx1,'MassAction');
par1 = addparameter(rx1kl,'kf');
par1.Value = 0.1;
par1.ValueUnits = '1/minute';
v1 = addvariant(dm,'var1'); % add three variants
v2 = addvariant(dm,'var2');
v3 = addvariant(dm,'var3');


Sign in to comment.

Accepted Answer

Arthur Goldsipe
Arthur Goldsipe on 9 Oct 2019
I know it's confusing, and I'll try to explain what's going on. But let me start by saying that the behavior you're seeing is what we intended, and it's consistent with how handle objects generally behave in MATLAB. The one area where I think SimBiology could be better is that the name removevariant is a bit misleading. Perhaps you should think of it as extractvariant instead.
Here's the bottom line: If you want to delete a variant, then call the delete method. removevariant is intended to extract a variant from a model, making it "standalone," so that if you delete the model it no longer deletes the variant. Once a variant is standalone, it will only get deleted when (1) you explicitly call delete on it or (2) the last reference to it goes away. So the reason that removevariant doesn't delete the variants is because you've stored references to them in variables like mcv. So if you simply clear the variable mcv after calling removevariant, the variant will get deleted. (I can show you a trick for how to "see" that, if you think it would be helpful.)
Likewise, after calling removevariant, mcv is different from mc.Variant because mcv was never in sync with mcv.Variant. Instead, if an object exists in both mcv and in mc.Variant, you can change the object via one, and see the change in the other (because they both refer to the same object). Maybe it would help you see if the difference if you realize that you could just as easily set mcv to just the first variant with mcv=mc.Variants(1), or even to a collection of variants from different models with mcv=[model1.Variants; model2.Variants].
Now, if you delete a variant contained in a SimBiology model, the model is smart enough to remove it from its list of variants. That's not something that happens with "normal" variables. For example, the variant does not get removed from mcv. Instead, that element of the vector shows up as a "handle to deleted Variant." That's pretty standard MATLAB behavior: Deleting a handle object results in a "deleted" object rather than remove it from any list. You normally have to remove it from a vector the way you remove any element from a vector. For example, you can use the special [] syntax: mcv(2) = [].
I think your concern is keeping mcv in sync with mc.Variants. Unforutunately, there's no real way to guarantee that. And the same is true with a doing something like mcp=mc.Parameters. (You alluded to some difference between variants and parameters. But the only difference I can think of is that parameters cannot be standalone, so there's no removeparameter method.)
Without knowing more about what you're trying to do and why you want to keep things in sync, I don't quite know what to recommend, other than to always accessing the property directly via the model if there's any chance it could have gotten modified.


Jim Bosley
Jim Bosley on 10 Oct 2019
As always, I thank you for your response to my long-winded questions.
You're right. I am thoroughly confused.
i'm writing new variants over old ones, with the same name, and in so doing want to avoid error messages if (for example) I say
when variant Blah already exists. This because some of my stuff is repetitive, and so a variant of final state values from one run will be overwritten by those of a next run. So I'd like to be able to easily delete variants. I also clone and merge variants and have written scripts to do so.
Here's the application: I have a model with three meals a day. So there is no "Steady state value" of the ICs. This means running a model out for a while (about a year of simulation time). I can create a base vp variant using the dashboard. Create new, add compartments, add initial values, add parameters. Call this Base_VP.
Now suppose I want different virtual patients (VPs) to represent different phenotypes. I create a variant with a relatively small number (maybe 20) of parameters that the model is sensitive to, or that biology tells me are relevant. Call this variant Perturb_Pheno_1. Then I run the model with VP_Base applied first, and Perturb_Pheno_1 applied to overwrite the specific parameters of interest. Then I create a variant end_IC_Pheno_1 using addcontent to add the the values of each Species at the end of simulation as 'intialAmount's. Then I can do this:
mergevar(mc,'Perturb_Pheno_1','end_IC_Pheno_1', 'VP_Pheno_1'); % with my script
which creates a variant VP_Pheno_1 containing the parameters I've perturbed to get phenotype 1, and the pseudo-steady-state values of the species to use as initial conditions. This gives me a compact representation of parameters deviating from base values, and the resulting pseudo-steady-state initial Amounts.
Simulating the VP with good initial conditions is as simple as running a sim with variants
VP_Base and VP_Pheno_1. This gives me a stable start to simulations with no equilibriation time required.
Some of the work involves manual tweaking of perturbation parameters (for example, to ensure that par values and outputs remain biologically feasible values) and so scripts are rerun. So if I have a Variant end_IC_Pheno_1 or VP_Pheno_1 already created, creating a new variant with the same name fails.
The point is, I'm using handles to modify the model object. For most stuff, modifying the object using the handle works fine: the handle reflects the change in the actual sbioroot object. But when using variants or doses, if I delete an element (one variant or one dose) using the handle, the handle is corrupted: it doesn't reflect the sbioroot object.
So mcv = mc.Variants creates the handle and lists variant indices, names, and whether each variant is active. Let's say I have 25 variants. But if I say
and then type mcv, I get
mcv= handle: 25-by-1 % Not a handle to a variant array anymore!
Interestingly, referring to mcv(i) with any value from 1 to 25, excluding 17, returns the correct variant. mcv(17) returns mcv(17) = handle.
So mcv went from a handle of mc.Variants of type Simbiology Variant Array, to whatever a handle: 25-by-1 is. So running delete() (or removevariant) does indeed delete (or extract) the variant, but it also corrupts the handle.
Any downside to my workaround? That is, anytime I do a delete or removevariant using the handle mcv, I refresh the handle using
mcv = mc.Varaints
The upshot seems to be that for most operations, changing something in sbioroot using a handle does not affect the handle. But some operations (like delete or removevariant) render the handle unusable and one has to refresh it. Yes?
Arthur Goldsipe
Arthur Goldsipe on 10 Oct 2019
A few quick thoughts:
Calling "delete(mcv(17));" doesn't invalidate mcv. It just makes the 17th element of that vector a "handle to a deleted object". This probably wasn't obvious because mcv wasn't displaying like a normal variant vector, but in R2019b the display changed to something that hopefully makes this less confusing. Anyway, you can still change properties on mcv(1), and it will update the variant anywhere it's referenced (including on the model).
And yes, if you want mcv to always match model.Variants, the safest thing is to do "mcv=model.Variants" after you delete or remove a variant. I don't really see any downside to that approach. But just for completeness, another alternative is to manually update mcv whenever you delete/remove. So, doing something like "delete(mcv(17)); mcv(17)=[];" should also keep mcv in sync with model.Variants.
Also, if part of the problem is that you want to have multiple variants with the same name, you can do that as long as they are "standalone", that is, they are not stored on the model. So the following is perfectly valid:
variant1 = sbiovariant('v'); % Then add some content
variant2 = sbiovariant('v'); % Then add some content
sbiosimulate(modelObj, [variant1 variant2]);
Finally, I want you to know that we are thinking pretty hard about how people use variants and about what works well and what doesn't. We know some things are painful and confusing, and we do want to come up something that is easier to use.
Jim Bosley
Jim Bosley on 10 Oct 2019
Again, Thanks Arthur. I hope my long verbose comment to your answer was helpful in understanding how at least one user uses variants. Your replies have been really, really helpful to me.

Sign in to comment.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!