Skip to content

Commit

Permalink
initial changes to RawTVDFunction
Browse files Browse the repository at this point in the history
  • Loading branch information
rileyjmurray committed Nov 20, 2024
1 parent 916877c commit d500a54
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 35 deletions.
8 changes: 5 additions & 3 deletions pygsti/algorithms/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1007,11 +1007,13 @@ def _do_runopt(objective, optimizer, printer):
tm = _time.time()
nDataParams = objective.num_data_params() # TODO - cache this somehow in term-based calcs...
profiler.add_time("run_gst_fit: num data params", tm)

chi2_k_qty = opt_result.chi2_k_distributed_qty # total chi2 or 2*deltaLogL
desc = objective.description
chi2_k_qty = opt_result.chi2_k_distributed_qty # total chi2 or 2*deltaLogL
if chi2_k_qty > 0:
# reject GST model if p-value < threshold (~0.05?)
pvalue = 1.0 - _stats.chi2.cdf(chi2_k_qty, nDataParams - nModelParams)
pvalue = 1.0 - _stats.chi2.cdf(chi2_k_qty, nDataParams - nModelParams)
else:
pvalue = 0.0
printer.log("%s = %g (%d data params - %d (approx) model params = expected mean of %g; p-value = %g)" %
(desc, chi2_k_qty, nDataParams, nModelParams, nDataParams - nModelParams, pvalue), 1)

Expand Down
77 changes: 45 additions & 32 deletions pygsti/objectivefns/objectivefns.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import sys as _sys
import time as _time
import pathlib as _pathlib
import warnings as _warnings

import numpy as _np

Expand Down Expand Up @@ -557,6 +558,8 @@ def lsvec(self, probs, counts, total_counts, freqs, intermediates=None):
"""
return _np.sqrt(self.terms(probs, counts, total_counts, freqs, intermediates))

# Infinite loop in evaluation of "dterms" and "dlsvec".

def dterms(self, probs, counts, total_counts, freqs, intermediates=None):
"""
Compute the derivatives of the terms of this objective function.
Expand Down Expand Up @@ -591,8 +594,9 @@ def dterms(self, probs, counts, total_counts, freqs, intermediates=None):
"""
if intermediates is None:
intermediates = self._intermediates(probs, counts, total_counts, freqs)
return 2 * self.lsvec(probs, counts, total_counts, freqs, intermediates) \
* self.dlsvec(probs, counts, total_counts, freqs, intermediates)
u = self.lsvec(probs, counts, total_counts, freqs, intermediates)
v = self.dlsvec(probs, counts, total_counts, freqs, intermediates)
return 2 * u * v

def dlsvec(self, probs, counts, total_counts, freqs, intermediates=None):
"""
Expand Down Expand Up @@ -2753,35 +2757,37 @@ def zero_freq_hterms(self, total_counts, probs):
return 2 * _np.ones(len(probs))


# The log(Likelihood) within the Poisson picture is: # noqa
# # noqa
# L = prod_{i,sl} lambda_{i,sl}^N_{i,sl} e^{-lambda_{i,sl}} / N_{i,sl}! # noqa
# # noqa
# Where lamba_{i,sl} := p_{i,sl}*N[i] is a rate, i indexes the operation sequence, # noqa
# and sl indexes the spam label. N[i] is the total counts for the i-th circuit, and # noqa
# so sum_{sl} N_{i,sl} == N[i]. We can ignore the p-independent N_j! and take the log: # noqa
# # noqa
# log L = sum_{i,sl} N_{i,sl} log(N[i]*p_{i,sl}) - N[i]*p_{i,sl} # noqa
# = sum_{i,sl} N_{i,sl} log(p_{i,sl}) - N[i]*p_{i,sl} (where we ignore the p-independent log(N[i]) terms) # noqa
# # noqa
# The objective function computes the negative log(Likelihood) as a vector of leastsq # noqa
# terms, where each term == sqrt( N_{i,sl} * -log(p_{i,sl}) + N[i] * p_{i,sl} ) # noqa
# # noqa
# See LikelihoodFunctions.py for details on patching # noqa
# The log(Likelihood) within the standard picture is:
#
# L = prod_{i,sl} p_{i,sl}^N_{i,sl}
#
# Where i indexes the operation sequence, and sl indexes the spam label.
# N[i] is the total counts for the i-th circuit, and
# so sum_{sl} N_{i,sl} == N[i]. We take the log:
#
# log L = sum_{i,sl} N_{i,sl} log(p_{i,sl})
#
# The objective function computes the negative log(Likelihood) as a vector of leastsq
# terms, where each term == sqrt( N_{i,sl} * -log(p_{i,sl}) )
#
# See LikelihoodFunction.py for details on patching
"""
The log(Likelihood) within the Poisson picture is: # noqa
# noqa
L = prod_{i,sl} lambda_{i,sl}^N_{i,sl} e^{-lambda_{i,sl}} / N_{i,sl}! # noqa
# noqa
Where lamba_{i,sl} := p_{i,sl}*N[i] is a rate, i indexes the operation sequence, # noqa
and sl indexes the spam label. N[i] is the total counts for the i-th circuit, and # noqa
so sum_{sl} N_{i,sl} == N[i]. We can ignore the p-independent N_j! and take the log: # noqa
# noqa
log L = sum_{i,sl} N_{i,sl} log(N[i]*p_{i,sl}) - N[i]*p_{i,sl} # noqa
= sum_{i,sl} N_{i,sl} log(p_{i,sl}) - N[i]*p_{i,sl} (where we ignore the p-independent log(N[i]) terms) # noqa
# noqa
The objective function computes the negative log(Likelihood) as a vector of leastsq # noqa
terms, where each term == sqrt( N_{i,sl} * -log(p_{i,sl}) + N[i] * p_{i,sl} ) # noqa
# noqa
See LikelihoodFunctions.py for details on patching # noqa
The log(Likelihood) within the standard picture is:
L = prod_{i,sl} p_{i,sl}^N_{i,sl}
Where i indexes the operation sequence, and sl indexes the spam label.
N[i] is the total counts for the i-th circuit, and
so sum_{sl} N_{i,sl} == N[i]. We take the log:
log L = sum_{i,sl} N_{i,sl} log(p_{i,sl})
The objective function computes the negative log(Likelihood) as a vector of leastsq
terms, where each term == sqrt( N_{i,sl} * -log(p_{i,sl}) )
See LikelihoodFunction.py for details on patching
"""
class RawPoissonPicDeltaLogLFunction(RawObjectiveFunction):
"""
The function `N*f*log(f/p) - N*(f-p)`.
Expand Down Expand Up @@ -4018,6 +4024,9 @@ def __init__(self, regularization=None,
resource_alloc=None, name='tvd', description="Total Variational Distance (TVD)", verbosity=0):
super().__init__(regularization, resource_alloc, name, description, verbosity)

def chi2k_distributed_qty(self, objective_function_value):
return -1

def terms(self, probs, counts, total_counts, freqs, intermediates=None):
"""
Compute the terms of the objective function.
Expand Down Expand Up @@ -4083,7 +4092,11 @@ def dterms(self, probs, counts, total_counts, freqs, intermediates=None):
numpy.ndarray
A 1D array of length equal to that of each array argument.
"""
raise NotImplementedError("Derivatives not implemented for TVD yet!")
_warnings.warn('This derivative is discontinuous and does not return a full subgradient.')
t = self.terms(probs, counts, total_counts, freqs, intermediates)
d = 0.5*_np.ones_like(t)
d[t < 0] *= -1
return d

def hterms(self, probs, counts, total_counts, freqs, intermediates=None):
"""
Expand Down

0 comments on commit d500a54

Please sign in to comment.