Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/matlab-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: MATLAB Tests

on:
push:
branches: ["**"]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v2

- name: Run unit tests
uses: matlab-actions/run-command@v2
with:
command: "addpath(genpath(pwd)); results = runtests_qrlab; assert(all([results.Passed] | [results.Incomplete]));"
21 changes: 12 additions & 9 deletions Coherence/FlagPoleState.m
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
function state = FlagPoleState(dim, p)
% Construct a flag-pole state in the computational basis.
%FLAGPOLESTATE Construct a flag-pole pure state density matrix.
% STATE = FLAGPOLESTATE(DIM, P) returns the density matrix
% |psi><psi| where
%
% .. math::
% \ket{\psi} = \sqrt{p}\,\ket{0} + \sum_{i=1}^{d-1} \sqrt{\frac{1-p}{d-1}}\,\ket{i},
% .. math::
% \ket{\psi} = \sqrt{p}\,\ket{0} + \sum_{i=1}^{d-1} \sqrt{\frac{1-p}{d-1}}\,\ket{i},
%
% so that :math:`\rho = \ket{\psi}\!\bra{\psi}` is a pure state.
% so that :math:`\rho = \ket{\psi}\!\bra{\psi}` is a pure state.
%
% Args:
% dim (numeric): System dimension :math:`d \ge 2`.
Expand All @@ -13,9 +15,10 @@
% Returns:
% state (matrix): Density matrix :math:`\rho = \ket{\psi}\!\bra{\psi}` of the flag-pole state.

ket = [sqrt(p)];
for i = 1:dim-1;
ket(end + 1) = sqrt((1-p)/(dim-1));
end
validateattributes(dim, {'numeric'}, {'scalar','integer','>=',2}, mfilename, 'dim', 1);
validateattributes(p, {'numeric'}, {'scalar','real','>=',0,'<=',1}, mfilename, 'p', 2);

state = ket.'*ket;
ampTail = sqrt((1 - p) / (dim - 1));
ket = [sqrt(p); ampTail * ones(dim - 1, 1)];
state = ket * ket.';
end
105 changes: 73 additions & 32 deletions Entanglement/compute_entanglement_measures.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
function result = compute_entanglement_measures(rho_in, opts)
% COMPUTE_ENTANGLEMENT_MEASURES
% Dispatcher for common entanglement measures. Supports selective
% evaluation, custom extensions, basic preprocessing (Hermitization and
% trace normalization), and optional tabular output.
%COMPUTE_ENTANGLEMENT_MEASURES Evaluate selected entanglement measures.
% RESULT = COMPUTE_ENTANGLEMENT_MEASURES(RHO_IN) evaluates default
% measures for a bipartite state matrix.
%
% RESULT = COMPUTE_ENTANGLEMENT_MEASURES(RHO_IN, OPTS) allows control of
% dimensions, selected measures, preprocessing behavior, and output type.
%
% Inputs:
% rho_in : Density matrix (state) or channel Choi matrix.
Expand All @@ -19,90 +21,129 @@
% result : struct with fields per measure (value, elapsed) plus a meta field,
% or a table with columns Measure, Value, Elapsed when opts.return_table is true.

% ---------- Option handling ----------
if ~exist('opts','var') || isempty(opts)
validateattributes(rho_in, {'numeric'}, {'2d','nonempty'}, mfilename, 'rho_in', 1);
if size(rho_in, 1) ~= size(rho_in, 2)
error('QRLab:Entanglement:NonSquareInput', 'rho_in must be a square matrix.');
end

if nargin < 2 || isempty(opts)
opts = struct();
elseif ~isstruct(opts)
error('QRLab:Entanglement:InvalidOptions', 'opts must be a struct when provided.');
end

% ---------- Option handling ----------
if ~isfield(opts, 'dims'); opts.dims = []; end
if ~isfield(opts, 'measures'); opts.measures = {'LogNeg','RainsBound','MaxRainsEntropy','TemperedLogNeg'}; end
if ~isfield(opts, 'custom_measures'); opts.custom_measures = struct(); end
if ~isfield(opts, 'return_table'); opts.return_table = false; end
if ~isfield(opts, 'normalize_trace'); opts.normalize_trace = true; end

if ~iscellstr(opts.measures) && ~(iscell(opts.measures) && all(cellfun(@(x) isstring(x) || ischar(x), opts.measures)))
error('QRLab:Entanglement:InvalidMeasureList', 'opts.measures must be a cell array of measure names.');
end
if ~islogical(opts.return_table) || ~isscalar(opts.return_table)
error('QRLab:Entanglement:InvalidReturnTableOption', 'opts.return_table must be a scalar logical.');
end
if ~islogical(opts.normalize_trace) || ~isscalar(opts.normalize_trace)
error('QRLab:Entanglement:InvalidNormalizeOption', 'opts.normalize_trace must be a scalar logical.');
end
if ~isstruct(opts.custom_measures)
error('QRLab:Entanglement:InvalidCustomMeasures', 'opts.custom_measures must be a struct of function handles.');
end

if ~isempty(opts.dims)
validateattributes(opts.dims, {'numeric'}, {'vector','numel',2,'integer','positive'}, mfilename, 'opts.dims');
end
if ~isfield(opts,'dims'); opts.dims = []; end
if ~isfield(opts,'measures'); opts.measures = {'LogNeg','RainsBound','MaxRainsEntropy','TemperedLogNeg'}; end
if ~isfield(opts,'custom_measures'); opts.custom_measures = struct(); end
if ~isfield(opts,'return_table'); opts.return_table = false; end
if ~isfield(opts,'normalize_trace'); opts.normalize_trace = true; end

t_start = tic;

% ---------- preprocess ----------
rho = rho_in;
if opts.normalize_trace
rho = (rho + rho')/2; % Hermitian
rho = (rho + rho') / 2;
tr_val = trace(rho);
if abs(tr_val) > eps
rho = rho / tr_val; % Normalize
if abs(tr_val) <= eps
warning('QRLab:Entanglement:NearZeroTrace', ...
'Trace is near zero during normalization; matrix is left unscaled.');
else
rho = rho / tr_val;
end
end

% ---------- Dimension inference ----------
if isempty(opts.dims)
d = sqrt(max(size(rho)));
d = sqrt(size(rho, 1));
if abs(d - round(d)) > 1e-10
error('Automatic dimension inference failed: matrix size is not a perfect square. Specify opts.dims manually.');
error('QRLab:Entanglement:DimensionInferenceFailed', ...
'Automatic dimension inference failed: matrix size is not a perfect square. Specify opts.dims manually.');
end
dims = round(d) * [1, 1];
dims = [round(d), round(d)];
else
dims = opts.dims;
end
if isempty(dims)
error('System dimensions are undefined. Please set opts.dims.');

if prod(dims) ~= size(rho, 1)
error('QRLab:Entanglement:DimensionMismatch', ...
'prod(opts.dims) must match matrix dimension. Received dims [%d %d] for %d-by-%d matrix.', ...
dims(1), dims(2), size(rho, 1), size(rho, 2));
end

% ---------- Built-in measure map ----------
available.LogNeg = @(rho,d) LogNeg(rho, d);
available.RainsBound = @(rho,d) RainsBound(rho, d);
available.MaxRainsEntropy = @(rho,d) MaxRains(rho, d);
available.TemperedLogNeg = @(rho,d) TempLogNeg(rho, d);
available.LogNeg = @(state, d) LogNeg(state, d);
available.RainsBound = @(state, d) RainsBound(state, d);
available.MaxRainsEntropy = @(state, d) MaxRains(state, d);
available.TemperedLogNeg = @(state, d) TempLogNeg(state, d);

% ---------- Merge custom measures ----------
measure_map = available;
user_fields = fieldnames(opts.custom_measures);
for k = 1:numel(user_fields)
name = user_fields{k};
measure_map.(name) = opts.custom_measures.(name);
fn = opts.custom_measures.(name);
if ~isa(fn, 'function_handle')
error('QRLab:Entanglement:InvalidCustomMeasure', ...
'Custom measure "%s" must be a function handle f(rho, dims).', name);
end
measure_map.(name) = fn;
end

% ---------- Evaluation ----------
measures_requested = opts.measures;
measures_requested = cellfun(@char, opts.measures, 'UniformOutput', false);
result_struct = struct();
for idx = 1:numel(measures_requested)
name = measures_requested{idx};
if ~isfield(measure_map, name)
warning('Measure %s is not registered and will be skipped.', name);
warning('QRLab:Entanglement:UnknownMeasure', ...
'Measure "%s" is not registered and will be skipped.', name);
continue;
end

fn = measure_map.(name);
t_local = tic;
value = fn(rho, dims);
result_struct.(name) = struct('value', value, 'elapsed', toc(t_local));
end

result_struct.meta = struct(...
result_struct.meta = struct( ...
'dims', dims, ...
'trace', trace(rho), ...
'elapsed_total', toc(t_start));

% ---------- Output ----------
if opts.return_table
measure_names = fieldnames(result_struct);
measure_names = measure_names(~strcmp(measure_names,'meta'));
values = zeros(numel(measure_names), 1);
measure_names = measure_names(~strcmp(measure_names, 'meta'));
values = zeros(numel(measure_names), 1);
elapsed = zeros(numel(measure_names), 1);
for k = 1:numel(measure_names)
m = measure_names{k};
values(k) = result_struct.(m).value;
values(k) = result_struct.(m).value;
elapsed(k) = result_struct.(m).elapsed;
end
result = table(measure_names, values, elapsed, ...
'VariableNames', {'Measure','Value','Elapsed'});
'VariableNames', {'Measure', 'Value', 'Elapsed'});
else
result = result_struct;
end
end
end
40 changes: 30 additions & 10 deletions Supermap/LinkProd.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,36 @@
% % Link product of two Choi matrices JA and JB:
% Jout = LinkProd(JA, JB, [Ain, Aout, Bin, Bout]);

validateattributes(JA, {'numeric'}, {'2d','nonempty'}, mfilename, 'JA', 1);
validateattributes(JB, {'numeric'}, {'2d','nonempty'}, mfilename, 'JB', 2);
validateattributes(DIM, {'numeric'}, {'vector','numel',4,'integer','positive'}, mfilename, 'DIM', 3);


% link Aout and Bin
Ain = DIM(1);
Aout = DIM(2);
Bin = DIM(3);
Bout = DIM(4);
Ain = DIM(1);
Aout = DIM(2);
Bin = DIM(3);
Bout = DIM(4);

assert(Aout == Bin, 'Output dimension of channel A does not match with the input dimension of channel B');
expectedJA = Ain * Aout;
expectedJB = Bin * Bout;

Link = kron(JA, eye(Bout)) * kron(eye(Ain), PartialTranspose(JB,1,[Bin, Bout]));
Jout = PartialTrace(Link, 2, [Ain, Aout, Bout]);
end
if ~isequal(size(JA), [expectedJA, expectedJA])
error('QRLab:Supermap:InvalidJA', ...
'JA must be a square matrix of size %d-by-%d for DIM=[%d %d %d %d].', ...
expectedJA, expectedJA, Ain, Aout, Bin, Bout);
end

if ~isequal(size(JB), [expectedJB, expectedJB])
error('QRLab:Supermap:InvalidJB', ...
'JB must be a square matrix of size %d-by-%d for DIM=[%d %d %d %d].', ...
expectedJB, expectedJB, Ain, Aout, Bin, Bout);
end

if Aout ~= Bin
error('QRLab:Supermap:DimensionMismatch', ...
'Output dimension of channel A (%d) must match input dimension of channel B (%d).', Aout, Bin);
end

% Link A_out and B_in.
link = kron(JA, eye(Bout)) * kron(eye(Ain), PartialTranspose(JB, 1, [Bin, Bout]));
Jout = PartialTrace(link, 2, [Ain, Aout, Bout]);
end
45 changes: 45 additions & 0 deletions docs/SoftwareDescription.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# QRLab Software Description

## Product Summary
QRLab is a MATLAB toolbox for research and education in quantum information processing and quantum resource theory. The package provides optimization-based and analytical routines for evaluating resource measures and simulating resource-aware transformations.

## Functional Modules

### Entanglement Module
- Static entanglement quantifiers (for example logarithmic negativity and Rains-type bounds).
- Dynamic/channel-oriented measures and capacity-related routines.
- Dispatcher utility (`compute_entanglement_measures`) for multi-measure batch evaluation.

### Coherence Module
- Coherence robustness computations.
- Coherence-relevant state constructors and simulation helpers.

### Magic Module
- Qubit and qudit magic resource measures.
- Representative state constructors and support data for stabilizer-related workflows.

### Quasi-Theory Module
- Probabilistic and observable-dependent error cancellation.
- Decomposition and virtual recovery routines for mitigation pipelines.

### Supermap Module
- Quantum switch implementations (Kraus and Choi representations).
- Link product construction for channel composition in Choi form.

### Utilities
- Foundational linear-algebra primitives and helper routines used across modules.

## Technical Characteristics
- **Language**: MATLAB
- **External dependencies**: QETLAB 0.9, CVX 2.1 (module-dependent)
- **Design style**: functional MATLAB scripts/functions organized by resource-theory domain

## Intended Use
- Algorithm prototyping in quantum information science.
- Reproducible numerical experiments in resource theory.
- Classroom/lab demonstrations of convex optimization and channel/state measures.

## Reliability and Maintainability Measures
- Input validation and standardized diagnostics in core utility paths.
- Reproducible examples under `examples/`.
- Automated smoke tests with `matlab.unittest` in `tests/` and unified runner `runtests_qrlab.m`.
55 changes: 55 additions & 0 deletions docs/UserGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# QRLab User Guide

## Overview
QRLab is a MATLAB toolbox for quantum resource theory workflows, including entanglement, coherence, magic, supermaps, and quasi-probability modules.

## Prerequisites
- MATLAB desktop
- QETLAB 0.9 on MATLAB path
- CVX 2.1 initialized with `cvx_setup`

## Getting Started
1. Add dependencies and QRLab to your path:
```matlab
addpath(genpath('path/to/QETLAB-0.9'));
addpath(genpath('path/to/QRLab'));
```
2. Run a smoke example:
```matlab
rho = MaxEntangled(2) * MaxEntangled(2)';
ln = LogNeg(rho);
fprintf('Logarithmic negativity: %.6f\n', ln);
```

## Core Workflows

### 1) Evaluate multiple entanglement measures in one call
```matlab
rho = MaxEntangled(2) * MaxEntangled(2)';
opts = struct();
opts.dims = [2 2];
opts.measures = {'LogNeg', 'RainsBound'};
res = compute_entanglement_measures(rho, opts);
disp(res.LogNeg.value);
```

### 2) Build a computational basis operator
```matlab
M = KetBra(4, 2, 3); % |2><3|
```

### 3) Generate a flag-pole coherence state
```matlab
rho = FlagPoleState(5, 0.6);
trace(rho) % should be 1
```

## Examples Folder
Use the scripts in `examples/` for quick demonstrations:
- `examples/example_basic_states.m`
- `examples/example_entanglement_dispatcher.m`

## Troubleshooting
- If `PartialTrace`, `PartialTranspose`, or `MaxEntangled` are unresolved, confirm QETLAB path setup.
- If CVX-based measures fail, run `cvx_setup` and ensure CVX 2.1 is active.
- For dependency-sensitive tests, use `runtests_qrlab` to see skipped tests with reasons.
13 changes: 13 additions & 0 deletions examples/example_basic_states.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
%% QRLab Example: Basic State Utilities
% Demonstrates utility and coherence helper functions that do not require CVX.

fprintf('--- example_basic_states ---\n');

% Build basis operator |2><3| in dimension 4.
M = KetBra(4, 2, 3);
fprintf('Non-zero entries in KetBra result: %d\n', nnz(M));

% Build a flag-pole state and check physical properties.
rho = FlagPoleState(5, 0.6);
fprintf('Trace(flag-pole state) = %.6f\n', trace(rho));
fprintf('Hermitian check (norm(rho-rho'')) = %.3e\n', norm(rho - rho', 'fro'));
Loading