I’m trying to pass an object that contains particles to a function that uses comparison operators. Consider the following simple example:
using MonteCarloMeasurements
mutable struct Foo{T}
p::T
end
function add_one!(foo::Foo)
if foo.p > 1
foo.p += 1
end
end
Here, the mutable struct Foo contains a parameter p which I would like to work with particles. The function add_one! simply adds one to the parameter p if its greater than 1.
I would like to pass a Foo object that contains particles to the add_one! function such that each particle is compared to the >1 operator. The following code doesn’t work due to unsafe comparisons.
p = Particles([1, 2, 3])
foo = Foo(p)
add_one!(foo)
ERROR: Comparison of uncertain values using comparison mode safe failed...
The error above makes sense, however, I do not want to turn on unsafe comparison as I want the comparison to occur for each particle separately. Is this possible? I saw there was a with_workspace function, but I don’t properly understand how to use it. Thanks.
You have reached more or less the limit of what is possible to handle automatically with the current design of MCM. If you have a single function that you need to handle, the easiest way would probably be to implement that by hand for Foo{Particles}. If you have many such functions, code generation is an option.
MCM works by implementing a type that behaves like a real number, but internally the type actually represents several samples. Each function that acts on numbers is then overloaded to act on all samples. The problem appears in an expression like
if foo.p > 1
end
foo.p > 1 has to result in a boolean, but since foo.p represents N different values of a number, it’s not always clear what boolean to produce. For functions that take numbers as arguments (fun(x::Real)), we have a function register_primitive fun that says that when this function is called with uncertain numbers, you should call this function on each of the internal numbers, but when the uncertain numbers are stored in the field of a struct, it’s not as easy. For example, how should we overload fun(x::Foo) to properly handle the fact that one of the fields of foo is an uncertain number? Short of manually implementing a custom method of fun(x::Foo{Particles}) that is aware of the type Foo and the fact that it internally stores uncertain numbers, I’m not sure how to do it. To handle this, it would probably be better to implement the package as a compiler pass rather than with method overloading, but this is currently beyond me. MCM has some experimental functionality that uses run-time introspection to figure out that there are internal fields of type Particles, but this is very brittle and not recommended in general.
I agree that using full control flow is complicated, and that
if p > 1
...
end
should fail for p::Particle. However, using algebra on booleans should work properly: I bet that p > 1 should provide a Particle with all atoms being true or false. Then using algebra should work:
p = p + (p > 1) # this should work
If it does not, then maybe adding a few methods to the package might be enough, and you could open an issue for that. Sometimes, algebra on booleans in enough to express a lot of stuff!