Tomographer  v1.1
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.

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::TrDistToRefCalculator defined in dmmhrw.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 TomoProblem_, typename DistValueType_ = double>
class HSDistToRefCalculator {
public:
typedef TomoProblem_ TomoProblem;
typedef typename TomoProblem::MatrQ MatrQ;
typedef typename MatrQ::MatrixType MatrixType;
// For ValueCalculator interface : value type
typedef DistValueType_ ValueType;
private:
MatrixType rho_ref;
public:
// Constructor, the reference state is 'rho_ref'
TrDistToRefCalculator(const TomoProblem & tomo, const MatrixType& rho_ref_)
: rho_ref(tomo.matq.initMatrixType())
{
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(const MatrixType & 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

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.

Note
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, where the other include directives are.

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 a callable which creates and returns a ValueCalculator instance:
[&](const OurTomoProblem & tomo) {
return MyCustomFigureOfMeritValueCalculatorInstance<...>(...);
},
logger); // and finally the logger instance
return;
}

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

  • The object "opt->valtype.ref_obj_name" is an std::string of anything the user specified in the config file as second part to the "--value-type" option (e.g. 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::force_pos_semidef() 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:

if (opt->valtype.valtype == val_type_spec::HS_DIST) {
MatrixType rho_ref = matq.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::force_pos_semidef(rho_ref, 1e-12);
// emit debug message; this is displayed in verbose mode
logger.debug("tomorun_dispatch()", [&](std::ostream & str) {
str << "Using 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 anonymous lambda function (C++11 feature) which returns a
// new instance of the figure of merit calculator.
[&rho_ref](const OurTomoProblem & tomo) {
return Tomographer::HsDistToRefCalculator<OurTomoProblem>(tomo, rho_ref);
},
// fourth argument: the logger object to emit log messages.
logger);
}