Tomographer  v4.0
Tomographer C++ Framework Documentation
Adding a new figure of merit to the tomorun program

The tomorun executable has several figures of merit built into the program: the trace distance, the purified distance, or the fidelity to any reference state, as well as the expectation value of an observable. If you wish to produce a histogram of a different figure of merit which can't be cast into one of these, you need to change the source code of the tomorun program.

Another option, which may be easier if you have a very special purpose which might not warrant inclusion into the generic tomorun program, is to combine the required classes into a new special-purpose program. This is not difficult, and there is already an example ready—see Creating a custom tomorun-like program.

This information page describes how you should change the source code of tomorun to include your figure of merit.

Note
If you perform modifications which may be useful to others, please fork the repository on github, perform your changes, and send me a pull request. This way your changes will be availble to other users who would like to use the Tomographer project. Please see here for information on how to contribute.

We'll illustrate these steps with a simple example: the two-norm distance (aka. the Hilbert-Schmidt distance) to any reference state, defined by \( d_\mathrm{HS}(\rho,\rho_{\mathrm{Ref}}) = \Vert \rho-\rho_{\mathrm{Ref}}\Vert_2 \), with \( \Vert A\Vert_2 = \mathrm{tr}\left(A^\dagger A\right) \).

1. Code how to calculate your figure of merit

First, you should write the code which calculates the figure of merit, complying to a ValueCalculator Interface type interface. Your new class should in particular have a method getValue(const MatrixType & T) taking as argument an Eigen matrix type which will be a matrix square root (see T Parameterization) of the quantum state rho for which the function should calculate the figure of merit.

You should do this in a new header file, that's the easiest.

For our example, we can get inspired by the code for, e.g., Tomographer::DenseDM::TSpace::TrDistToRefCalculator defined in tspacefigofmerit.h. For example, let's create a file called "hs_dist.h" inside the "cxx/tomorun/" directory of the tomographer project:

#ifndef HS_DIST_H
#define HS_DIST_H
#include <Eigen/Eigen>
template<typename DMTypes_, typename DistValueType_ = double>
class HSDistToRefCalculator
: public virtual Tomographer::Tools::NeedOwnOperatorNew<typename DMTypes_::MatrixType>::ProviderType
{
public:
typedef DMTypes_ DMTypes;
// this is the type to use to store a dense (dim x dim) matrix:
typedef typename DMTypes::MatrixType MatrixType;
typedef typename DMTypes::MatrixTypeConstRef MatrixTypeConstRef;
// For the ValueCalculator interface: the value type
typedef DistValueType_ ValueType;
private:
const MatrixType rho_ref;
public:
// Constructor, the reference state is 'rho_ref'
TrDistToRefCalculator(MatrixTypeConstRef rho_ref_)
: rho_ref(rho_ref_)
{
}
// This method actually calculates the given value. The argument "T" is
// the T-parameterization of the density matrix rho, which is simply a
// square root of rho. (This is indeed the representation used during the
// random walk.)
inline ValueType getValue(MatrixTypeConstRef T) const
{
// rho is obtained with T*T.adjoint(). With Eigen, the HS-norm (Frobenius) of a
// matrix is given by A.norm().
return (T*T.adjoint() - rho_ref).norm();
}
};
#endif // HS_DIST_H

The part about needownoperatornew.h and Tomographer::Tools::NeedOwnOperatorNew is to make sure that the object, when created, is aligned in memory. This is needed because the object has a Eigen member (rho_ref) which must be aligned in memory for vectorized operations (see Eigen's docs for alignment issues, there's a lot on that). In the Tomographer project, the Tomographer::Tools::NeedOwnOperatorNew virtual base makes sure that the necessary operator new() is defined so that objects are aligned in memory when allocated.

2. Declare the new figure of merit as command line option value

The choice of figure of merit is specified as the argument to the command line option "--value-type". So you should in fact first decide of a short memo string describing your figure of merit (e.g. for our example "HS-dist" would be an appropriate choice).

Now, open the file "tomorun_opts.h" located inside the "cxx/tomorun/" directory. The locations where you should adapt the code are marked by comments saying "INSERT CUSTOM FIGURE OF MERIT HERE". Adapt the code as follows.

The class val_type_spec is a type which is used to store the choice of figure of merit. It stores the choice as an enum value, but it is capable of converting to and from a string. So you should add a new enumeration value, as well as adapt the class methods, so that it understands your new figure of merit.

As with the other figures of merit, the user may also pass a string argument in the form "HS-dist:string_argument_goes_here" which can specify for example a reference state. You don't have to do anything special for that, it's taken care of for you already.

Also change the command-line help text to include documentation for your new figure of merit.

In our example, we'd change the following enumeration inside the class val_type_spec from:

enum ValueType {
INVALID = 0,
OBS_VALUE,
TR_DIST,
FIDELITY,
PURIF_DIST
};

to:

enum ValueType {
INVALID = 0,
OBS_VALUE,
TR_DIST,
FIDELITY,
PURIF_DIST,
HS_DIST // new figure of merit: HS distance to some reference state
};

Then we would insert the following inside the method val_type_spec::set_value_string(), after the similar tests for the other figures of merit:

if (valtype_str == "HS-dist") {
valtype = HS_DIST;
ref_obj_name = ref_obj_name_str; // the reference state
return;
}

and similarly, we'd update the function operator<<(std::ostream & str, const val_type_spec & val) to include a case for our figure of merit:

case val_type_spec::HS_DIST:
str << "HS-dist";
break;

3. Instruct tomorun how to instantiate your figure of merit calculator

The final piece of code which needs to be added is the logic of how your class which calculates the figure of merit (in our example, inside "hs_dist.h") should be instantiated.

The relevant file to modify is the file named tomorun_dispatch.h, located inside the "cxx/tomorun/" directory.

First, include your "hs_dist.h" file near the top: insert the line

#include "hs_dist.h"

near the top of the file, below the other include directives.

Then you'll have to code how specifically the program should instantiate your value calculator. Add a conditional in the function tomorun_dispatch() which tests whether the user asked for your figure of merit, and instantiate your class appropriately. The general logic should look like this:

if (opt->valtype.valtype == val_type_spec::<MY_CUSTOM_FIGURE_OF_MERIT>) {
... instantiate ValueCalculator and dispatch to tomorun<...>(...) ...
// Finally, dispatch the execution to tomorun<>(...):
tomorun<BinningAnalysisErrorBars>(
tomodat, // the TomoProblem instance
opt, // the program options
// and our ValueCalculator instance:
MyCustomFigureOfMeritValueCalculator<...>(...),
logger); // and finally the logger instance
return;
}

Starting in Tomographer version 2, the set up is slightly more complicated, because there are two different ways tomorun can be compiled. It can either be compiled by using a Tomographer::MultiplexorValueCalculator, or with static checks and different static code for each figure of merit (as before). The former approach seems more efficient, and is now the default (this can be changed with CMake options when compiling tomorun). The logic is not that compicated, so you're best off by seeing what the code does for the built-in figures of merit and mimicking that.

You're probably best off copying from the built-in examples inside that same function, or the example for the Hilbert-Schmidt distance presented here. Here are some additional tips:

  • The object "opt->valtype.ref_obj_name" is an std::string of anything the user specified at the command line or in the config file as second part to the "--value-type" option (e.g., representing the name of the variable in the MATLAB data file containing the reference state density matrix).
  • You can load data from the MATLAB data file via the "matf" object, which is a Tomographer::MAT::File instance.
  • You may of course use any tool provided by Eigen and the Tomographer API, for example Tomographer::Tools::forcePosSemiDef() to ensure a matrix is positive semidefinite.

In our example for the Hilbert-Schmidt distance, the reference state is read from the MATLAB data file. It is to be taken by default to be "rho_MLE", which is the maximum likelihood estimate state, if "--value-type=HS-dist". A custom reference state can be given which is to be read from the MATLAB data file (if "--value-type=HS-dist:my_ref_state_dm", where "my_ref_state_dm" is the name of the variable inside the MATLAB data file containing the density matrix of the reference state). Here's the code for the non-multiplexed version of the value-calculator:

if (opt->valtype.valtype == val_type_spec::HS_DIST) {
MatrixType rho_ref(dmt.initMatrixType());
// determine the variable name of the reference state. By default, "rho_MLE".
std::string refname = "rho_MLE";
if (opt->valtype.ref_obj_name.size()) {
// explicit reference state given in the command-line option
refname = opt->valtype.ref_obj_name;
}
// read the reference state from the MATLAB data file into the matrix 'rho_ref'
rho_ref = Tomographer::MAT::value<MatrixType>(matf->var(refname));
// make sure that all eigenvalues of rho_ref are positive.
rho_ref = Tomographer::Tools::forcePosSemiDef(rho_ref, 1e-12);
// emit debug message; this is displayed in verbose mode
logger.debug("tomorun_dispatch()", [&](std::ostream & str) {
str << "Using HS distance figure of merit with rho_ref = \n"
<< rho_ref << "\n";
});
// finally, dispatch the execution to the main function tomorun().
tomorun<BinningAnalysisErrorBars>(
// first argument: the tomography problem data and types
tomodat,
// second argument: the command-line options
opt,
// thrid argument: an instance of the figure of merit calculator
HsDistToRefCalculator<DMTypes>(rho_ref),
// fourth argument: the logger object to emit log messages
logger);
}