How to constraint multiplication of certain parameters to constant value in lmfit? - constraints

I have a model with several parameters, say "A", "B" and "C". They all are set varying with some min and max bounds, motivated by their representation of physical quantities.
In addition, I'd need to constraint their product to certain value (say 1), such as "A" * "B" * "C" == 1.
I tried:
p.add('mult_constraint", expr = 'A*B*C==1)
,but that throws an error:
NameError: at expr='<ast.Module object at 0x0000014A546EAFD0>'
How would constraint like this need to be written?

I think what you want to do is to define three parameters A, B, and C, with the constraint that ABC=1. That could look like:
import lmfit
params = lmfit.Parameters()
params.add('A', value=0.75, min=0)
params.add('B', value=5.50, min=0.25)
params.add('C', expr='1/(A*B)')
That is, the constraint means that one of your parameters is not actually varying independently but determined by the constraint and the values of the other parameters.

Related

Understanding the event and evidence parameters in bnlearns cpquery

I followed the example for cpquery(fitted, event, evidence, cluster, method = "ls", ..., debug = FALSE) given in the documentation:
#rm(list=ls())
library(bnlearn)
data(learning.test)
fitted = bn.fit(hc(learning.test), learning.test)
# the result should be around 0.025.
cpquery(fitted, (B == "b"), (A == "a"))
# out: 0.02692665
It worked as advertised, but I have some confusion about the syntax. For reference, head(learning.test) looks like this:
A B C D E F
1 b c b a b b
2 b a c a b b
3 a a a a a a
4 a a a a b b
5 a a b c a a
6 c c a c c a
and the total number of rows is 5000.
Now to my confusion: The second argument to cpquery, (B == "b"), is passed to the event parameter and the third argument (A == "a") is passed to the evidence parameter. The documentation says about these two parameters that
The event and evidence arguments must be two expressions describing the event of interest and the conditioning evidence in a format such that, if we denote with data the data set the network was learned from, data[evidence, ] and data[event, ] return the correct observations.
I didn't quite understand how the syntax (B == "b") worked, since I thought B is not defined in the current scope. So given what was said in the documentation above, I took data to be learning.test in my case and then I tried to do learning.test[(B == "b"),], which indeed resulted in the error
Error in `[.data.frame`(learning.test, (B == "b"), ) :
object 'B' not found
Given the documentation I thought this line of code should've worked.
I then tried to do learning.test[(learning.test$B=="b"),] which does seem to return a data frame with all rows where the B-column has the value "b". I then tried to use this syntax in the cpquery function:
cpquery(fitted, (learning.test$B == "b"), (learning.test$A == "a"))
but to my surprise this resulted in the error
Error in sampling(fitted = fitted, event = event, evidence = evidence, :
logical vector for evidence is of length 5000 instead of 10000.
I don't understand why I get this error (other than that something goes wrong in the logical sampling used by cpquery), since as far as I can see I am conforming to the quoted passage from the documentation.
So in conclusion I wonder why does the example from the documentation work when it seems to not conform to the quoted passage from the documentation, and why does my modification not work even though it seems to conform to the quoted passage in the documentation? (I am not very familiary with R so maybe this is very basic).
I think that this section is just trying to show what the evidence and event expressions represent in the original data and how they relate to logic sampling. e.g. if you subset the original data with the event/evidence, then it will subset the data to only contains rows that agree with the event/evidence.
But you are correct that the notation data[evidence, ] won't work. You can uselearning.test[eval(expression(B == "b"), learning.test), ], which may help you understand the intent. This is similar to what used to appear in older help files.
For logic sampling (the default inference method) a bunch of samples are generated from the BN, the default is N=10000 samples (*). So 1) you have estimated a parameterised BN, 2) draw N samples from it, 3) subset this sample according to event / evidence vectors to calculate some conditional probability (hopefully correctness is not lost in my brevity).
cpquery(fitted, (learning.test$B == "b"), (learning.test$A == "a")) doesn't work as the default number of samples in the sampled BN (step 2. above) is 10000 but learning.test has 5000 rows (and so the logical vector produced by (learning.test$B == "b") has length 5000). You can force your approach to work, (by work I mean produce an answer), by setting the n parameter , cpquery(fitted, (learning.test$B == "b"), (learning.test$A == "a"), n=5000) but this will not always do what you expect: you are evaluating the event/evidence expression against the observed data instead of the simulated data. I think that if this sort of logical vector is supplied then cpquery should probably throw an error. Best to stick with the documented approaches.
(*) From the help I'd expect the default number of samples to be 5000 * log10(nparams(fitted))) =~ 8000, but perhaps batch = 10000 takes precedence.

Why I got the wrong mean value in R?

I am trying to calculate the mean of -0.9643991 and -0.6756494, but for some reasons, the mean function returns the value of the first number; as shown below:
> a = -0.9643991
> b = -0.6756494
> mean(a, b)
[1] -0.9643991
What is the issue here?
mean takes the mean of its first argument, a vector. Additional arguments let you set preferences, which can depend on the class of the first argument, such as ignoring missing values. If you want the mean of a and b, you need to put them together in a vector, using c(). Like this:
mean(c(a, b))

select string array values based on another array

I can select data that is equal to a value with
data = rand(1:3, 10)
value = 2
data .== value
or equal to a list of values with
values = [1, 2]
in.(data, (values,))
The last one is generic and also works for a scalar: in.(data, (value, )) .
However, this works for Int, but the generic does not work for String values:
data = rand(["A", "B", "C"], 10)
value = "B"
data .== value
values = ["A","B"]
in.(data, (values, ))
in.(data, (value, ))
ERROR: use occursin(x, y) for string containment
Is there a generic way for Strings?
For a generic val input I'm now writing the following, but I feel there must be a better solution.
isa(val, AbstractArray) ? in.(data, (val,)) : data .== val
Background: I'm creating a function to select rows from a dataframe (and do something with them) but I want to allow for both a list of values as well as a single value.
Here is a trick that is worth knowing:
[x;]
Now - if x is an array it will remain an array. If x is a scalar it will become a 1-element array. And this is exactly what you need.
So you can write
in.(data, ([val;],))
The drawback is that it allocates a new array, but I guess that val is small and it is not used in performance critical code? If the code is performance critical I think it is better to treat scalars and arrays by separate branches.

Is this the expected behavior

My apologies if this is kind of vague question as I am new to R. While experimenting with R I found one weird behavior. When I create a function like:
myfunction <- function(a,b){
print(a,b)
}
and call it like:
myfunction(b = 10, a = 20)
it returns with result 20, but if I simply call it without function via assigning it directly to variables like:
a <- 20
b <- 10
print(a, b)
I get an error:
Error in print.default(a, b) : invalid 'digits' argument
Furthermore I have read that printing multiple variables in the same line can be accomplished via:
sprintf("%i %i",a, b)
So here is it a bug that it is appearing in function call with result as the first argument?
It might be revealing some underlying differences in how parameters are being handled in different scenarios but I don't think it's a bug.
If your intention is to print both values, consider changing:
print(a,b)
To something like:
print(paste(a,b))
From ?print.default:
# S3 method for default
print(x, digits = NULL, quote = TRUE,
na.print = NULL, print.gap = NULL, right = FALSE,
max = NULL, useSource = TRUE, …)
x the object to be printed.
digits a non-null value for digits specifies the minimum number of
significant digits to be printed in values. The default, NULL, uses
getOption("digits"). (For the interpretation for complex numbers see
signif.) Non-integer values will be rounded down, and only values
greater than or equal to 1 and no greater than 22 are accepted.
...
So R is expecting everything that you actually want to print to be contained in the first variable (x).
Based on your results and some of the comments, apparently in some cases the second variable is being accepted as a valid digits parameter value and in other cases it is not.
While this is a little odd, the more important point is that print(a,b) is not a syntactically correct way to print multiple values.

R- distinguishing argument values

Hi I want to pass a list of arguments into my main function to use two sub functions.
f<-function(a,...){
x1<-f1(...)
x2<-f2(...)
}
Suppose f1 takes an argument with name "a" and f2 takes an argument with name "a". How can I solve this problem. The name "a" is used inside the main function and the two subfunctions. I am trying to distinguish what name "a" is for different functions but it seems to be a very difficult task.
I can give a more specific example
f<-function(x,...){
print(mean(x))
x1<-dnorm(...)
x2<-dbinom(...)
}
Obviously, dnorm and dbinom use name "x" as inputs. But, I want to use a different value of x for each of the sub functions. Furthermore, I want to use name "x" inside the main function to calculate it's mean because the main x is a vector.
Since they have the same name, you'll need some way of distinguishing them or they will simply clash, as you've pointed out. There's really not much magic beyond that: you've spotted the issue.
You'll also need a way of keeping dbinom-specific arguments out of dnorm, because dnorm throws an error if you give it a size argument, for example.
You can write out all the relevant args, for example:
f<-function(x,dnx, mean=0, sd=1, dnlog=FALSE, dbx, size, prob, dblog=FALSE, ...){
print(mean(x))
x1<-dnorm(x=dnx, mean, sd, log=dnlog)
x2<-dbinom(x=dbx, size, prob, log=dblog)
}
or supply them as lists:
f<-function(x,
dn_args=list(x=0, mean = 0, sd = 1, log = FALSE),
db_args=list(x=5, size=10, prob=0.5, log = FALSE), ...){
print(mean(x))
x1<-do.call(dnorm, dn_args)
x2<-do.call(dbinom, db_args)
}
You can also consider whether you need to refactor the function into smaller pieces. :)

Resources