Create Custom Tolerance

This example shows how to create a custom tolerance to determine if two DNA sequences have a Hamming distance within a specified tolerance. For two DNA sequences of the same length, the Hamming distance is the number of positions in which the nucleotides (letters) of one sequence differ from the other.

In a file, DNA.m, in your working folder, create a simple class for a DNA sequence.

classdef DNA
    properties(SetAccess=immutable)
        Sequence
    end
    
    methods
        function dna = DNA(sequence)
            validLetters = ...
                sequence == 'A' | ...
                sequence == 'C' | ...
                sequence == 'T' | ...
                sequence == 'G';
            
            if ~all(validLetters(:))
                error('Sequence contained a letter not found in DNA.')
            end
            dna.Sequence = sequence;
        end
    end
end

In a file in your working folder, create a tolerance class so that you can test that DNA sequences are within a specified Hamming distance. The constructor requires a Value property that defines the maximum Hamming distance.

classdef HammingDistance < matlab.unittest.constraints.Tolerance
    properties
        Value
    end
    
    methods
        function tolerance = HammingDistance(value)
            tolerance.Value = value;
        end
    end
end

In a methods block with the HammingDistance class definition, include the following method so that the tolerance supports DNA objects. Tolerance classes must implement a supports method.

    methods
        function tf = supports(~, value)
            tf = isa(value, 'DNA');
        end
    end

In a methods block with the HammingDistance class definition, include the following method that returns true or false. Tolerance classes must implement a satisfiedBy method. The testing framework uses this method to determine if two values are within the tolerance.

    methods
        function tf = satisfiedBy(tolerance, actual, expected)
            if ~isSameSize(actual.Sequence, expected.Sequence)
                tf = false;
                return
            end
            tf = hammingDistance(actual.Sequence,expected.Sequence) <= tolerance.Value;
        end
    end

In the HammingDistance.m file, define the following helper functions outside of the classdef block. The isSameSize function returns true if two DNA sequences are the same size, and the hammingDistance function returns the Hamming distance between two sequences.

function tf = isSameSize(str1, str2)
tf = isequal(size(str1), size(str2));
end

function distance = hammingDistance(str1, str2)
distance = nnz(str1 ~= str2);
end

The function returns a Diagnostic object with information about the comparison. In a methods block with the HammingDistance class definition, include the following method that returns a StringDiagnostic. Tolerance classes must implement a getDiagosticFor method.

    methods
        function diag = getDiagnosticFor(tolerance, actual, expected)
            import matlab.unittest.diagnostics.StringDiagnostic
            
            if ~isSameSize(actual.Sequence, expected.Sequence)
                str = 'The DNA sequences must be the same length.';
            else
                str = sprintf('%s%d.\n%s%d.', ...
                    'The DNA sequences have a Hamming distance of ', ...
                    hammingDistance(actual.Sequence, expected.Sequence), ...
                    'The allowable distance is ', ...
                    tolerance.Value);
            end
            diag = StringDiagnostic(str);
        end
    end

 HammingDistance Class Definition Summary

At the command prompt, create a TestCase for interactive testing.

import matlab.unittest.TestCase
import matlab.unittest.constraints.IsEqualTo

testCase = TestCase.forInteractiveUse;

Create two DNA objects.

sampleA = DNA('ACCTGAGTA');
sampleB = DNA('ACCACAGTA');

Verify that the DNA sequences are equal to each other.

testCase.verifyThat(sampleA, IsEqualTo(sampleB))
Interactive verification failed.

---------------------
Framework Diagnostic:
---------------------
IsEqualTo failed.
--> ObjectComparator failed.
    --> The objects are not equal using "isequal".

Actual Object:
      DNA with properties:
    
        Sequence: 'ACCTGAGTA'
Expected Object:
      DNA with properties:
    
        Sequence: 'ACCACAGTA'

Verify that the DNA sequences are equal to each other within a Hamming distance of 1.

testCase.verifyThat(sampleA, IsEqualTo(sampleB,...
    'Within', HammingDistance(1)))
Interactive verification failed.

---------------------
Framework Diagnostic:
---------------------
IsEqualTo failed.
--> ObjectComparator failed.
    --> The objects are not equal using "isequal".
    --> The DNA sequences have a Hamming distance of 2.
        The allowable distance is 1.

Actual Object:
      DNA with properties:
    
        Sequence: 'ACCTGAGTA'
Expected Object:
      DNA with properties:
    
        Sequence: 'ACCACAGTA'

The sequences are not equal to each other within a tolerance of 1. The testing framework displays additional diagnostics from the getDiagnosticFor method.

Verify that the DNA sequences are equal to each other within a Hamming distance of 2.

testCase.verifyThat(sampleA, IsEqualTo(sampleB,...
    'Within', HammingDistance(2)))
Interactive verification passed.

See Also