Instruction:
Can you generate the documentation for the following Python 3 function
def _coral_to_seqfeature(feature):
    '''Convert a coral.Feature to a Biopython SeqFeature.

    :param feature: coral Feature.
    :type feature: coral.Feature

    '''
    bio_strand = 1 if feature.strand == 1 else -1
    ftype = _process_feature_type(feature.feature_type, bio_to_coral=False)
    sublocations = []
    if feature.gaps:
        # There are gaps. Have to define location_operator and  add subfeatures
        location_operator = 'join'
        # Feature location means nothing for 'join' sequences?
        # TODO: verify
        location = FeatureLocation(ExactPosition(0), ExactPosition(1),
                                   strand=bio_strand)
        # Reconstruct start/stop indices for each subfeature
        stops, starts = zip(*feature.gaps)
        starts = [feature.start] + [start + feature.start for start in starts]
        stops = [stop + feature.start for stop in stops] + [feature.stop]
        # Build subfeatures
        for start, stop in zip(starts, stops):
            sublocation = FeatureLocation(ExactPosition(start),
                                          ExactPosition(stop),
                                          strand=bio_strand)
            sublocations.append(sublocation)
        location = CompoundLocation(sublocations, operator='join')
    else:
        # No gaps, feature is simple
        location_operator = ''
        location = FeatureLocation(ExactPosition(feature.start),
                                   ExactPosition(feature.stop),
                                   strand=bio_strand)
    qualifiers = feature.qualifiers
    qualifiers['label'] = [feature.name]
    seqfeature = SeqFeature(location, type=ftype,
                            qualifiers=qualifiers,
                            location_operator=location_operator)
    return seqfeature

Response: Convert a coral. Feature to a Biopython SeqFeature.


Instruction:
Given the following Python 3 function, write the documentation
def as_ord_matrix(matrix, alphabet):
    '''Given the SubstitutionMatrix input, generate an equivalent matrix that
    is indexed by the ASCII number of each residue (e.g. A -> 65).'''
    ords = [ord(c) for c in alphabet]
    ord_matrix = np.zeros((max(ords) + 1, max(ords) + 1), dtype=np.integer)
    for i, row_ord in enumerate(ords):
        for j, col_ord in enumerate(ords):
            ord_matrix[row_ord, col_ord] = matrix[i, j]

    return ord_matrix

Response: Given the SubstitutionMatrix input generate an equivalent matrix that contains the ASCII number of each residue.


Instruction:
How would you code a function in Python 3 to
calculate the alignment of two sequences.

Response:
def aligner(seqj, seqi, method='global', gap_open=-7, gap_extend=-7,
            gap_double=-7, matrix=submat.DNA_SIMPLE.matrix,
            alphabet=submat.DNA_SIMPLE.alphabet):
    '''Calculates the alignment of two sequences. The global method uses
    a global Needleman-Wunsh algorithm, local does a a local
    Smith-Waterman alignment, global_cfe does a global alignment with
    cost-free ends and glocal does an alignment which is global only with
    respect to the shorter sequence, also known as a semi-global
    alignment. Returns the aligned (sub)sequences as character arrays.

    Gotoh, O. (1982). J. Mol. Biol. 162, 705-708.
    Needleman, S. & Wunsch, C. (1970). J. Mol. Biol. 48(3), 443-53.
    Smith, T.F. & Waterman M.S. (1981). J. Mol. Biol. 147, 195-197.

    :param seqj: First sequence.
    :type seqj: str
    :param seqi: Second sequence.
    :type seqi: str
    :param method: Type of alignment: 'global', 'global_cfe', 'local', or
                   'glocal'.
    :type method: str
    :param gap_open: The cost of opening a gap (negative number).
    :type gap_open: float
    :param gap_extend: The cost of extending an open gap (negative number).
    :type gap_extend: float
    :param gap_double: The gap-opening cost if a gap is already open in the
                       other sequence (negative number).
    :type gap_double: float
    :param matrix: A score matrix dictionary name. Examples can be found in
                   the substitution_matrices module.
    :type matrix: str
    :param alphabet: The characters corresponding to matrix rows/columns.
    :type alphabet: str

    '''
    amatrix = as_ord_matrix(matrix, alphabet)
    NONE, LEFT, UP, DIAG = range(4)  # NONE is 0
    max_j = len(seqj)
    max_i = len(seqi)

    if max_j > max_i:
        flip = 1
        seqi, seqj = seqj, seqi
        max_i, max_j = max_j, max_i
    else:
        flip = 0

    F = np.zeros((max_i + 1, max_j + 1), dtype=np.float32)
    I = np.ndarray((max_i + 1, max_j + 1), dtype=np.float32)
    I.fill(-np.inf)
    J = np.ndarray((max_i + 1, max_j + 1), dtype=np.float32)
    J.fill(-np.inf)
    pointer = np.zeros((max_i + 1, max_j + 1), dtype=np.uint)  # NONE

    if method == 'global':
        pointer[0, 1:] = LEFT
        pointer[1:, 0] = UP
        F[0, 1:] = gap_open + gap_extend * np.arange(0, max_j,
                                                     dtype=np.float32)
        F[1:, 0] = gap_open + gap_extend * np.arange(0, max_i,
                                                     dtype=np.float32)
    elif method == 'global_cfe':
        pointer[0, 1:] = LEFT
        pointer[1:, 0] = UP
    elif method == 'glocal':
        pointer[0, 1:] = LEFT
        F[0, 1:] = gap_open + gap_extend * np.arange(0, max_j,
                                                     dtype=np.float32)

    seqi_ord = [ord(base) for base in seqi]
    seqj_ord = [ord(base) for base in seqj]
    for i in range(1, max_i + 1):
        ci = seqi_ord[i - 1]
        for j in range(1, max_j + 1):
            cj = seqj_ord[j - 1]
            # I
            I[i, j] = max(F[i, j - 1] + gap_open,
                          I[i, j - 1] + gap_extend,
                          J[i, j - 1] + gap_double)
            # J
            J[i, j] = max(F[i - 1, j] + gap_open,
                          J[i - 1, j] + gap_extend,
                          I[i - 1, j] + gap_double)
            # F
            diag_score = F[i - 1, j - 1] + amatrix[ci, cj]
            left_score = I[i, j]
            up_score = J[i, j]
            max_score = max(diag_score, up_score, left_score)

            F[i, j] = max(0, max_score) if method == 'local' else max_score

            if method == 'local':
                if F[i, j] == 0:
                    pass  # point[i,j] = NONE
                elif max_score == diag_score:
                    pointer[i, j] = DIAG
                elif max_score == up_score:
                    pointer[i, j] = UP
                elif max_score == left_score:
                    pointer[i, j] = LEFT
            elif method == 'glocal':
                # In a semi-global alignment we want to consume as much as
                # possible of the longer sequence.
                if max_score == up_score:
                    pointer[i, j] = UP
                elif max_score == diag_score:
                    pointer[i, j] = DIAG
                elif max_score == left_score:
                    pointer[i, j] = LEFT
            else:
                # global
                if max_score == up_score:
                    pointer[i, j] = UP
                elif max_score == left_score:
                    pointer[i, j] = LEFT
                else:
                    pointer[i, j] = DIAG

    align_j = []
    align_i = []
    if method == 'local':
        # max anywhere
        i, j = max_index(F)
    elif method == 'glocal':
        # max in last col
        i, j = (F[:, -1].argmax(), max_j)
    elif method == 'global_cfe':
        # from i,j to max(max(last row), max(last col)) for free
        row_max, col_idx = F[-1].max(), F[-1].argmax()
        col_max, row_idx = F[:, -1].max(), F[:, -1].argmax()
        if row_max > col_max:
            pointer[-1, col_idx + 1:] = LEFT
        else:
            pointer[row_idx + 1:, -1] = UP

    p = pointer[i, j]
    while p != NONE:
        if p == DIAG:
            i -= 1
            j -= 1
            align_j.append(seqj[j])
            align_i.append(seqi[i])
        elif p == LEFT:
            j -= 1
            align_j.append(seqj[j])
            align_i.append('-')
        elif p == UP:
            i -= 1
            align_j.append('-')
            align_i.append(seqi[i])
        else:
            raise Exception('wtf!')
        p = pointer[i, j]
    align_i = ''.join(align_i[::-1])
    align_j = ''.join(align_j[::-1])
    # np.array(align_i.reverse())
    return ((align_i, align_j) if flip else (align_j, align_i))


Instruction:
Can you tell what is the following Python 3 function doing
def score_alignment(a, b, gap_open, gap_extend, matrix):
    '''Calculate the alignment score from two aligned sequences.

    :param a: The first aligned sequence.
    :type a: str
    :param b: The second aligned sequence.
    :type b: str
    :param gap_open: The cost of opening a gap (negative number).
    :type gap_open: int
    :param gap_extend: The cost of extending an open gap (negative number).
    :type gap_extend: int.
    :param matrix: A score matrix dictionary name. Examples can be found in
                   the substitution_matrices module.

    '''
    al = a
    bl = b
    l = len(al)
    score = 0
    assert len(bl) == l, 'Alignment lengths must be the same'
    mat = as_ord_matrix(matrix)

    gap_started = 0

    for i in range(l):
        if al[i] == '-' or bl[i] == '-':
            score += gap_extend if gap_started else gap_open
            gap_started = 1
        else:
            score += mat[ord(al[i]), ord(bl[i])]
            gap_started = 0
    return score

Response: Calculate the alignment score from two aligned sequences.


Instruction:
Can you tell what is the following Python 3 function doing
def build_docs(directory):
    os.chdir(directory)
    process = subprocess.Popen(["make", "html"], cwd=directory)
    process.communicate()

Response: Builds sphinx docs from a given directory.


Instruction:
Can you create a Python 3 function that
designs Gibson primers given two DNA sequences.

Response:
def gibson_primers(dna1, dna2, overlap='mixed', maxlen=80, overlap_tm=65.0,
                   insert=None, primer_kwargs=None):
    '''Design Gibson primers given two DNA sequences (connect left to right)

    :param dna1: First piece of DNA for which to design primers. Once Gibsoned,
                 would be connected at its right side to dna2.
    :type dna1: coral.DNA
    :param dna2: First piece of DNA for which to design primers. Once Gibsoned,
                 would be connected at its right side to dna2.
    :type dna2: coral.DNA
    :param overlap: Specifies location of overlap. 'left' puts it on the 'dna1'
                    side (i.e. the primer to amplify dna2). 'right' puts it on
                    the dna2 side, and 'mixed' does a ~50:50 split
    :type overlap: str
    :param maxlen: Maximum length of each primer.
    :type maxlen: int
    :param overlap_tm: Minimum Tm of overlap
    :type overlap_tm: float
    :param insert: A DNA insert to add with primers and use as assembly
                   homology. This overrides the 'split' argument.
    :type insert: coral.DNA
    :param primer_kwargs: keyword arguments to pass to design_primer()
    :type primer_kwargs: dict
    :returns: Reverse, then forward primer for bridging the two sequences.
              Note that the forward primer binds dna2, reverse dna1.
    :rtype: A sequence.Primer tuple
    :raises: ValueError if split parameter is an invalid string.

    '''
    if primer_kwargs is None:
        primer_kwargs = {}

    # Annealing sequences
    # DNA 2 primer is a forward primer
    fwd_anneal = coral.design.primer(dna2, **primer_kwargs)
    # DNA 1 primer is a reverse primer
    rev_anneal = coral.design.primer(dna1.flip(), **primer_kwargs)
    # Overhangs
    if insert is None:
        # No insert, so follow split argument
        if overlap == 'left':
            # If splitting left, put overhang on forward primer
            overlap_revcomp = coral.design.primer(dna1.flip(), tm=overlap_tm,
                                                  tm_undershoot=0)
            fwd_overhang = overlap_revcomp.primer().reverse_complement()
            rev_overhang = None
        elif overlap == 'right':
            # If splitting right, put overhang on reverse primer
            overlap = coral.design.primer(dna2, tm=overlap_tm, tm_undershoot=0)
            fwd_overhang = None
            rev_overhang = overlap.primer().reverse_complement()
        elif overlap == 'mixed':
            # If mixed, grow size of both until overlap Tm is reached
            overlap_l = dna1[0:0]  # Empty sequence.DNA
            overlap_r = dna2[0]  # First base
            overlap_melt = overlap_r.tm()
            while overlap_melt < overlap_tm:
                rlen = len(overlap_r)
                llen = len(overlap_l)
                if rlen > llen:
                    # Increase left side of overlap
                    overlap_l = dna1[-(rlen + 1):]
                else:
                    # Increase right side of overlap
                    overlap_r = dna2[:(llen + 1)]
                overlap = overlap_l + overlap_r
                overlap_melt = overlap.tm()
            fwd_overhang = overlap_l
            rev_overhang = overlap_r.reverse_complement()
        else:
            raise ValueError('split argument must be left, right, or mixed')
        # Generate primers using anneal, overhang, and tm data
        fwd = coral.Primer(fwd_anneal.primer(), tm=fwd_anneal.tm,
                           overhang=fwd_overhang)
        rev = coral.Primer(rev_anneal.primer(), tm=rev_anneal.tm,
                           overhang=rev_overhang)
    else:
        # There's an insert to use as the overhang
        overlap = insert
        fwd_overhang = insert.primer()
        rev_overhang = insert.reverse_complement().primer()
        # Generate primers using anneal, overhang, and tm data
        fwd = coral.Primer(fwd_anneal.primer(), tm=fwd_anneal.tm,
                           overhang=fwd_overhang)
        rev = coral.Primer(rev_anneal.primer(), tm=rev_anneal.tm,
                           overhang=rev_overhang)
        left_trim = 0
        # If either primer is too long, try trimming the overhang
        while len(fwd) > maxlen:
            # Generate new overlap
            overlap = insert[left_trim:]
            # Tm must be above overlap_tm
            if coral.analysis.tm(overlap) < overlap_tm:
                raise TmError('Right primer is too long with this Tm setting.')
            # Regenerate forward overhang
            fwd_overhang = overlap.primer()
            # Regenerate primer with new overhang
            fwd = coral.Primer(fwd_anneal.primer(), tm=fwd_anneal.tm,
                               overhang=fwd_overhang)
            # Increase 'trimming' index
            left_trim += 1
        right_trim = 0
        while len(rev) > maxlen:
            # Generate new overlap
            overlap = insert[:len(insert) - right_trim]
            # Tm must be above overlap_tm
            if coral.analysis.tm(overlap) < overlap_tm:
                raise TmError('Left primer is too long with this Tm setting.')
            # Regenerate reverse overhang
            rev_overhang = overlap.reverse_complement().to_ss()
            rev = coral.Primer(rev_anneal.to_ss(), tm=rev_anneal.tm,
                               overhang=rev_overhang)
            # Increase 'trimming' index
            right_trim += 1
    # Check primer lengths
    if any([len(primer) > maxlen for primer in (fwd, rev)]):
        raise LengthError('At least one of the primers is longer than maxlen.')

    return rev, fwd


Instruction:
Write a Python 3 script to
design Gibson primers given a set of DNA sequences.

Response:
def gibson(seq_list, circular=True, overlaps='mixed', overlap_tm=65,
           maxlen=80, terminal_primers=True, primer_kwargs=None):
    '''Design Gibson primers given a set of sequences

    :param seq_list: List of DNA sequences to stitch together
    :type seq_list: list containing coral.DNA
    :param circular: If true, designs primers for making a circular construct.
                     If false, designs primers for a linear construct.
    :type circular: bool
    :param splits: Specifies locations of overlap. Must be either a single
                   entry of the same type as the 'split' parameter in
                   gibson_primers or a list of those types of the appropriate
                   length (for circular construct, len(seq_list), for
                   linear construct, len(seq_list) - 1)
    :type splits: str or list of str
    :param overlap_tm: Minimum Tm of overlap
    :type overlap_tm: float
    :param maxlen: Maximum length of each primer.
    :type maxlen: int
    :param terminal_primers: If the output is not circular, will design
                             non-Gibson primers for amplifying the first and
                             last fragments sans homology. If False, there will
                             be one less set of primers returned.
    :type terminal_primers: bool
    :param primer_kwargs: keyword arguments to pass to design.primer
    :type primer_kwargs: dict
    :returns: Forward and reverse primers for amplifying every fragment.
    :rtype: a list of sequence.Primer tuples
    :raises: ValueError if split parameter is an invalid string or wrong size.

    '''

    # Input checking
    if circular:
        n_overlaps = len(seq_list)
    else:
        n_overlaps = len(seq_list) - 1

    if type(overlaps) is str:
        overlaps = [overlaps] * n_overlaps
    else:
        if len(overlaps) != n_overlaps:
            raise ValueError('Incorrect number of \'overlaps\' entries.')
        else:
            for overlap in overlaps:
                if overlap not in ['left', 'right', 'mixed']:
                    raise ValueError('Invalid \'overlaps\' setting.')

    if primer_kwargs is None:
        primer_kwargs = {}

    # If here, inputs were good
    # Design primers for linear constructs:
    primers_list = []
    for i, (left, right) in enumerate(zip(seq_list[:-1], seq_list[1:])):
        primers_list.append(gibson_primers(left, right, overlaps[i],
                                           overlap_tm=overlap_tm,
                                           primer_kwargs=primer_kwargs))
    if circular:
        primers_list.append(gibson_primers(seq_list[-1], seq_list[0],
                                           overlaps[-1],
                                           overlap_tm=overlap_tm,
                                           primer_kwargs=primer_kwargs))
    else:
        if terminal_primers:
            primer_f = coral.design.primer(seq_list[0], **primer_kwargs)
            primer_r = coral.design.primer(seq_list[-1].reverse_complement(),
                                           **primer_kwargs)
            primers_list.append((primer_r, primer_f))

    # Primers are now in order of 'reverse for seq1, forward for seq2' config
    # Should be in 'forward and reverse primers for seq1, then seq2', etc
    # Just need to rotate one to the right
    flat = [y for x in primers_list for y in x]
    flat = [flat[-1]] + flat[:-1]
    grouped_primers = [(flat[2 * i], flat[2 * i + 1]) for i in
                       range(len(flat) / 2)]

    return grouped_primers


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _decompose(string, n):
    '''Given string and multiplier n, find m**2 decomposition.

    :param string: input string
    :type string: str
    :param n: multiplier
    :type n: int
    :returns: generator that produces m**2 * string if m**2 is a factor of n
    :rtype: generator of 0 or 1

    '''
    binary = [int(x) for x in bin(n)[2:]]
    new_string = string
    counter = 1
    while counter <= len(binary):
        if binary[-counter]:
            yield new_string
        new_string += new_string
        counter += 1

Response: Given a string and multiplier n find m**2 decomposition.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def reverse_complement(sequence, material):
    '''Reverse complement a sequence.

    :param sequence: Sequence to reverse complement
    :type sequence: str
    :param material: dna, rna, or peptide.
    :type material: str
    '''
    code = dict(COMPLEMENTS[material])
    reverse_sequence = sequence[::-1]
    return ''.join([code[str(base)] for base in reverse_sequence])

Response: Reverse complement a sequence.


Instruction:
How would you explain what the following Python 3 function does
def check_alphabet(seq, material):
    '''Verify that a given string is valid DNA, RNA, or peptide characters.

    :param seq: DNA, RNA, or peptide sequence.
    :type seq: str
    :param material: Input material - 'dna', 'rna', or 'pepide'.
    :type sequence: str
    :returns: Whether the `seq` is a valid string of `material`.
    :rtype: bool
    :raises: ValueError if `material` isn't \'dna\', \'rna\', or \'peptide\'.
             ValueError if `seq` contains invalid characters for its
             material type.

    '''
    errs = {'dna': 'DNA', 'rna': 'RNA', 'peptide': 'peptide'}
    if material == 'dna' or material == 'rna' or material == 'peptide':
        alphabet = ALPHABETS[material]
        err_msg = errs[material]
    else:
        msg = 'Input material must be \'dna\', \'rna\', or \'peptide\'.'
        raise ValueError(msg)
    # This is a bottleneck when modifying sequence - hence the run_checks
    # optional parameter in sequence objects..
    # First attempt with cython was slower. Could also try pypy.
    if re.search('[^' + alphabet + ']', seq):
        raise ValueError('Encountered a non-%s character' % err_msg)

Response: Verify that a given string is a valid DNA RNA or peptide character sequence.


Instruction:
How would you explain what the following Python 3 function does
def process_seq(seq, material):
    '''Validate and process sequence inputs.

    :param seq: input sequence
    :type seq: str
    :param material: DNA, RNA, or peptide
    :type: str
    :returns: Uppercase version of `seq` with the alphabet checked by
              check_alphabet().
    :rtype: str

    '''
    check_alphabet(seq, material)
    seq = seq.upper()
    return seq

Response: Validate and process sequence inputs.


Instruction:
Create a Python 3 function for
testing whether a sequence is a palindrome.

Response:
def palindrome(seq):
    '''Test whether a sequence is palindrome.

    :param seq: Sequence to analyze (DNA or RNA).
    :type seq: coral.DNA or coral.RNA
    :returns: Whether a sequence is a palindrome.
    :rtype: bool

    '''
    seq_len = len(seq)
    if seq_len % 2 == 0:
        # Sequence has even number of bases, can test non-overlapping seqs
        wing = seq_len / 2
        l_wing = seq[0: wing]
        r_wing = seq[wing:]
        if l_wing == r_wing.reverse_complement():
            return True
        else:
            return False
    else:
        # Sequence has odd number of bases and cannot be a palindrome
        return False


Instruction:
Can you tell what is the following Python 3 function doing
def copy(self):
        '''Create a copy of the current instance.

        :returns: A safely editable copy of the current sequence.

        '''
        # Significant performance improvements by skipping alphabet check
        return type(self)(self.seq, self.material, run_checks=False)

Response: Create a safely editable copy of the current instance.


Instruction:
Given the following Python 3 function, write the documentation
def locate(self, pattern):
        '''Find sequences matching a pattern.

        :param pattern: Sequence for which to find matches.
        :type pattern: str
        :returns: Indices of pattern matches.
        :rtype: list of ints

        '''
        if len(pattern) > len(self):
            raise ValueError('Search pattern longer than searchable ' +
                             'sequence.')
        seq = self.seq

        pattern = str(pattern).upper()
        re_pattern = '(?=' + pattern + ')'
        matches = [index.start() % len(self) for index in
                   re.finditer(re_pattern, seq)]

        return matches

Response: Find sequences matching a pattern.


Instruction:
Can you tell what is the following Python 3 function doing
def copy(self):
        '''Return a copy of the Feature.

        :returns: A safely editable copy of the current feature.
        :rtype: coral.Feature

        '''
        return type(self)(self.name, self.start, self.stop, self.feature_type,
                          gene=self.gene, locus_tag=self.locus_tag,
                          qualifiers=self.qualifiers, strand=self.strand)

Response: Return a safely editable copy of the current feature.


Instruction:
Can you create a Python 3 function that
splits Nupack commands over processors.

Response:
def nupack_multi(seqs, material, cmd, arguments, report=True):
    '''Split Nupack commands over processors.

    :param inputs: List of sequences, same format as for coral.analysis.Nupack.
    :type inpus: list
    :param material: Input material: 'dna' or 'rna'.
    :type material: str
    :param cmd: Command: 'mfe', 'pairs', 'complexes', or 'concentrations'.
    :type cmd: str
    :param arguments: Arguments for the command.
    :type arguments: str
    :returns: A list of the same return value you would get from `cmd`.
    :rtype: list

    '''
    nupack_pool = multiprocessing.Pool()
    try:
        args = [{'seq': seq,
                 'cmd': cmd,
                 'material': material,
                 'arguments': arguments} for seq in seqs]
        nupack_iterator = nupack_pool.imap(run_nupack, args)
        total = len(seqs)
        msg = ' calculations complete.'
        passed = 4
        while report:
            completed = nupack_iterator._index
            if (completed == total):
                break
            else:
                if passed >= 4:
                    print '({0}/{1}) '.format(completed, total) + msg
                    passed = 0
                passed += 1
                time.sleep(1)
        multi_output = [x for x in nupack_iterator]
        nupack_pool.close()
        nupack_pool.join()
    except KeyboardInterrupt:
        nupack_pool.terminate()
        nupack_pool.close()
        raise KeyboardInterrupt

    return multi_output


Instruction:
How would you code a function in Python 3 to
run picklable Nupack command.

Response:
def run_nupack(kwargs):
    '''Run picklable Nupack command.

    :param kwargs: keyword arguments to pass to Nupack as well as 'cmd'.
    :returns: Variable - whatever `cmd` returns.

    '''
    run = NUPACK(kwargs['seq'])
    output = getattr(run, kwargs['cmd'])(**kwargs['arguments'])
    return output


Instruction:
How would you explain what the following Python 3 function does
def pfunc_multi(self, strands, permutation=None, temp=37.0, pseudo=False,
                    material=None, dangles='some', sodium=1.0, magnesium=0.0):
        '''Compute the partition function for an ordered complex of strands.
        Runs the \'pfunc\' command.

        :param strands: List of strands to use as inputs to pfunc -multi.
        :type strands: list
        :param permutation: The circular permutation of strands to test in
                            complex. e.g. to test in the order that was input
                            for 4 strands, the permutation would be [1,2,3,4].
                            If set to None, defaults to the order of the
                            input strands.
        :type permutation: list
        :param temp: Temperature setting for the computation. Negative values
                     are not allowed.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :param dangles: How to treat dangles in the computation. From the
                        user guide: For \'none\': Dangle energies are ignored.
                        For \'some\': \'A dangle energy is incorporated for
                        each unpaired base flanking a duplex\'. For 'all': all
                        dangle energy is considered.
        :type dangles: str
        :param sodium: Sodium concentration in solution (molar), only applies
                       to DNA.
        :type sodium: float
        :param magnesium: Magnesium concentration in solution (molar), only
                          applies to DNA>
        :type magnesium: float
        :returns: A 2-tuple of the free energy of the ordered complex
                  (float) and the partition function (float).
        :rtype: tuple

        '''
        # Set the material (will be used to set command material flag)
        material = self._set_material(strands, material, multi=True)

        # Set up command flags
        cmd_args = self._prep_cmd_args(temp, dangles, material, pseudo, sodium,
                                       magnesium, multi=True)
        # Set up the input file and run the command
        if permutation is None:
            permutation = range(1, len(strands) + 1)
        lines = self._multi_lines(strands, permutation)
        stdout = self._run('pfunc', cmd_args, lines).split('\n')

        return (float(stdout[-3]), float(stdout[-2]))

Response: Compute the partition function for an ordered complex of strands.


Instruction:
Write a Python 3 function for
computing the pair probabilities for an ordered complex of strands.

Response:
def pairs(self, strand, cutoff=0.001, temp=37.0, pseudo=False,
              material=None, dangles='some', sodium=1.0, magnesium=0.0):
        '''Compute the pair probabilities for an ordered complex of strands.
        Runs the \'pairs\' command.

        :param strand: Strand on which to run pairs. Strands must be either
                       coral.DNA or coral.RNA).
        :type strand: list
        :param cutoff: Only probabilities above this cutoff appear in the
                       output.
        :type cutoff: float
        :param temp: Temperature setting for the computation. Negative values
                     are not allowed.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :param dangles: How to treat dangles in the computation. From the
                        user guide: For \'none\': Dangle energies are ignored.
                        For \'some\': \'A dangle energy is incorporated for
                        each unpaired base flanking a duplex\'. For 'all': all
                        dangle energy is considered.
        :type dangles: str
        :param sodium: Sodium concentration in solution (molar), only applies
                       to DNA.
        :type sodium: float
        :param magnesium: Magnesium concentration in solution (molar), only
                          applies to DNA>
        :type magnesium: float
        :returns: The probability matrix, where the (i, j)th entry
                  is the probability that base i is bound to base j. The matrix
                  is augmented (it's N+1 by N+1, where N is the number of bases
                  in the sequence) with an (N+1)th column containing the
                  probability that each base is unpaired.
        :rtype: numpy.array

        '''
        # Set the material (will be used to set command material flag)
        material = self._set_material(strand, material)

        # Set up command flags
        cmd_args = self._prep_cmd_args(temp, dangles, material, pseudo, sodium,
                                       magnesium, multi=False)

        # Set up the input file and run the command. Note: no STDOUT
        lines = [str(strand)]
        self._run('pairs', cmd_args, lines)

        # Read the output from file
        ppairs = self._read_tempfile('pairs.ppairs')
        data = re.search('\n\n\d*\n(.*)', ppairs, flags=re.DOTALL).group(1)
        N = len(strand)
        data_lines = [line.split('\t') for line in data.split('\n') if line]
        prob_matrix = self._pairs_to_np(data_lines, N)

        return prob_matrix


Instruction:
Can you write a function in Python 3 where it
computes the pair probabilities for a set of strands in a single base.

Response:
def pairs_multi(self, strands, cutoff=0.001, permutation=None, temp=37.0,
                    pseudo=False, material=None, dangles='some', sodium=1.0,
                    magnesium=0.0):
        '''Compute the pair probabilities for an ordered complex of strands.
        Runs the \'pairs\' command.

        :param strands: List of strands to use as inputs to pairs -multi.
        :type strands: list
        :param permutation: The circular permutation of strands to test in
                            complex. e.g. to test in the order that was input
                            for 4 strands, the permutation would be [1,2,3,4].
                            If set to None, defaults to the order of the
                            input strands.
        :type permutation: list
        :param temp: Temperature setting for the computation. Negative values
                     are not allowed.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :param dangles: How to treat dangles in the computation. From the
                        user guide: For \'none\': Dangle energies are ignored.
                        For \'some\': \'A dangle energy is incorporated for
                        each unpaired base flanking a duplex\'. For 'all': all
                        dangle energy is considered.
        :type dangles: str
        :param sodium: Sodium concentration in solution (molar), only applies
                       to DNA.
        :type sodium: float
        :param magnesium: Magnesium concentration in solution (molar), only
                          applies to DNA>
        :type magnesium: float
        :param cutoff: Only probabilities above this cutoff appear in the
                       output.
        :type cutoff: float
        :returns: Two probability matrices: The probability matrix as in the
                  pairs method (but with a dimension equal to the sum of the
                  lengths of the sequences in the permutation), and a similar
                  probability matrix where multiple strands of the same species
                  are considered to be indistinguishable.
        :rtype: list

        '''
        # Set the material (will be used to set command material flag)
        material = self._set_material(strands, material, multi=True)

        # Set up command flags
        cmd_args = self._prep_cmd_args(temp, dangles, material, pseudo, sodium,
                                       magnesium, multi=True)

        # Set up the input file and run the command. Note: no STDOUT
        if permutation is None:
            permutation = range(1, len(strands) + 1)
        lines = self._multi_lines(strands, permutation)
        self._run('pairs', cmd_args, lines)

        # Read the output from file
        N = sum([len(s) for s in strands])
        matrices = []
        for mat_type in ['ppairs', 'epairs']:
            data = self._read_tempfile('pairs.' + mat_type)
            probs = re.search('\n\n\d*\n(.*)', data, flags=re.DOTALL).group(1)
            lines = probs.split('\n')
            # Remove the last line (empty)
            lines.pop()
            pairlist = [line.split('\t') for line in lines]
            prob_matrix = self._pairs_to_np(pairlist, N)
            matrices.append(prob_matrix)

        return matrices


Instruction:
Can you generate a brief explanation for the following Python 3 code
def mfe(self, strand, degenerate=False, temp=37.0, pseudo=False,
            material=None, dangles='some', sodium=1.0, magnesium=0.0):
        '''Compute the MFE for an ordered complex of strands. Runs the \'mfe\'
        command.

        :param strand: Strand on which to run mfe. Strands must be either
                       coral.DNA or coral.RNA).
        :type strand: coral.DNA or coral.RNA
        :param degenerate: Setting to True will result in returning a list of
                           dictionaries associated with structures having the
                           same, minimal MFE value.
        :type degenerate: bool
        :param temp: Temperature setting for the computation. Negative values
                     are not allowed.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :param dangles: How to treat dangles in the computation. From the
                        user guide: For \'none\': Dangle energies are ignored.
                        For \'some\': \'A dangle energy is incorporated for
                        each unpaired base flanking a duplex\'. For 'all': all
                        dangle energy is considered.
        :type dangles: str
        :param sodium: Sodium concentration in solution (molar), only applies
                       to DNA.
        :type sodium: float
        :param magnesium: Magnesium concentration in solution (molar), only
                          applies to DNA>
        :type magnesium: float
        :returns: A dictionary with keys for 'mfe' (a float), 'dotparens'
                  (dot-parens notation of the MFE structure), and 'pairlist'
                  (a pair list notation of the MFE structure). Note that the
                  pair list will be an empty list if the MFE is unstructured.
        :rtype: dict

        '''
        # Set the material (will be used to set command material flag)
        material = self._set_material(strand, material)

        # Set up command flags
        cmd_args = self._prep_cmd_args(temp, dangles, material, pseudo, sodium,
                                       magnesium, multi=False)
        if degenerate:
            cmd_args.append('-degenerate')

        # Set up the input file and run the command. Note: no STDOUT
        lines = [str(strand)]
        self._run('mfe', cmd_args, lines)

        # Read the output from file
        structures = self._process_mfe(self._read_tempfile('mfe.mfe'))

        if degenerate:
            return structures
        else:
            return structures[0]

Response: Compute the MFE for an ordered complex of strands.


Instruction:
Can you tell what is the following Python 3 function doing
def mfe_multi(self, strands, permutation=None, degenerate=False, temp=37.0,
                  pseudo=False, material=None, dangles='some', sodium=1.0,
                  magnesium=0.0):
        '''Compute the MFE for an ordered complex of strands. Runs the \'mfe\'
        command.

        :param strands: Strands on which to run mfe. Strands must be either
                       coral.DNA or coral.RNA).
        :type strands: list
        :param permutation: The circular permutation of strands to test in
                            complex. e.g. to test in the order that was input
                            for 4 strands, the permutation would be [1,2,3,4].
                            If set to None, defaults to the order of the
                            input strands.
        :type permutation: list
        :param temp: Temperature setting for the computation. Negative values
                     are not allowed.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :param dangles: How to treat dangles in the computation. From the
                        user guide: For \'none\': Dangle energies are ignored.
                        For \'some\': \'A dangle energy is incorporated for
                        each unpaired base flanking a duplex\'. For 'all': all
                        dangle energy is considered.
        :type dangles: str
        :param sodium: Sodium concentration in solution (molar), only applies
                       to DNA.
        :type sodium: float
        :param magnesium: Magnesium concentration in solution (molar), only
                          applies to DNA>
        :type magnesium: float
        :param degenerate: Setting to True will result in returning a list of
                           dictionaries associated with structures having the
                           same, minimal MFE value.
        :type degenerate: bool
        :returns: A dictionary with keys for 'mfe' (a float), 'dotparens'
                  (dot-parens notation of the MFE structure), and 'pairlist'
                  (a pair list notation of the MFE structure). Note that the
                  pair list will be an empty list if the MFE is unstructured.
        :rtype: dict

        '''
        # Set the material (will be used to set command material flag)
        material = self._set_material(strands, material, multi=True)

        # Set up command flags
        cmd_args = self._prep_cmd_args(temp, dangles, material, pseudo, sodium,
                                       magnesium, multi=True)
        if degenerate:
            cmd_args.append('-degenerate')

        # Set up the input file and run the command. Note: no STDOUT
        if permutation is None:
            permutation = range(1, len(strands) + 1)
        lines = self._multi_lines(strands, permutation)
        self._run('mfe', cmd_args, lines)

        # Read the output from file
        structures = self._process_mfe(self._read_tempfile('mfe.mfe'))

        if degenerate:
            return structures
        else:
            return structures[0]

Response: Compute the MFE for an ordered complex of strands.


Instruction:
Make a summary of the following Python 3 code
def subopt(self, strand, gap, temp=37.0, pseudo=False, material=None,
               dangles='some', sodium=1.0, magnesium=0.0):
        '''Compute the suboptimal structures within a defined energy gap of the
        MFE. Runs the \'subopt\' command.

        :param strand: Strand on which to run subopt. Strands must be either
                       coral.DNA or coral.RNA).
        :type strand: coral.DNA or coral.RNA
        :param gap: Energy gap within to restrict results, e.g. 0.1.
        :type gap: float
        :param temp: Temperature setting for the computation. Negative values
                     are not allowed.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :param dangles: How to treat dangles in the computation. From the
                        user guide: For \'none\': Dangle energies are ignored.
                        For \'some\': \'A dangle energy is incorporated for
                        each unpaired base flanking a duplex\'. For 'all': all
                        dangle energy is considered.
        :type dangles: str
        :param sodium: Sodium concentration in solution (molar), only applies
                       to DNA.
        :type sodium: float
        :param magnesium: Magnesium concentration in solution (molar), only
                          applies to DNA>
        :type magnesium: float
        :returns: A list of dictionaries of the type returned by .mfe().
        :rtype: list

        '''
        # Set the material (will be used to set command material flag)
        material = self._set_material(strand, material)

        # Set up command flags
        cmd_args = self._prep_cmd_args(temp, dangles, material, pseudo, sodium,
                                       magnesium, multi=False)

        # Set up the input file and run the command. Note: no STDOUT
        lines = [str(strand), str(gap)]
        self._run('subopt', cmd_args, lines)

        # Read the output from file
        structures = self._process_mfe(self._read_tempfile('subopt.subopt'))

        return structures

Response: Compute the suboptimal structures within a defined energy gap of the MFE.


Instruction:
How would you code a function in Python 3 to
enumerate the total number of secondary structures over the the ensemble Ω ( π.

Response:
def count(self, strand, pseudo=False):
        '''Enumerates the total number of secondary structures over the
        structural ensemble Ω(π). Runs the \'count\' command.

        :param strand: Strand on which to run count. Strands must be either
                       coral.DNA or coral.RNA).
        :type strand: list
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :returns: The count of the number of structures in the structural
                  ensemble.
        :rtype: int

        '''
        # Set up command flags
        if pseudo:
            cmd_args = ['-pseudo']
        else:
            cmd_args = []

        # Set up the input file and run the command
        stdout = self._run('count', cmd_args, [str(strand)]).split('\n')

        # Return the count
        return int(float(stdout[-2]))


Instruction:
Create a Python 3 function to
enumerate the total number of secondary structures over the structureural ensemble Ω ( π ) with an ordered permutation of strands.

Response:
def count_multi(self, strands, permutation=None, pseudo=False):
        '''Enumerates the total number of secondary structures over the
        structural ensemble Ω(π) with an ordered permutation of strands. Runs
        the \'count\' command.

        :param strands: List of strands to use as inputs to count -multi.
        :type strands: list
        :param permutation: The circular permutation of strands to test in
                            complex. e.g. to test in the order that was input
                            for 4 strands, the permutation would be [1,2,3,4].
                            If set to None, defaults to the order of the
                            input strands.
        :type permutation: list
        :param temp: Temperature setting for the computation. Negative values
                     are not allowed.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :returns: Dictionary with the following key:value pairs: 'energy':
                  free energy, 'pfunc': partition function.
        :rtype: dict

        '''
        # Set up command flags
        cmd_args = ['-multi']
        if pseudo:
            cmd_args.append('-pseudo')

        # Set up the input file and run the command
        if permutation is None:
            permutation = range(1, len(strands) + 1)
        lines = self._multi_lines(strands, permutation)
        stdout = self._run('count', cmd_args, lines).split('\n')

        return int(float(stdout[-2]))


Instruction:
How would you explain what the following Python 3 function does
def energy(self, strand, dotparens, temp=37.0, pseudo=False, material=None,
               dangles='some', sodium=1.0, magnesium=0.0):
        '''Calculate the free energy of a given sequence structure. Runs the
        \'energy\' command.

        :param strand: Strand on which to run energy. Strands must be either
                       coral.DNA or coral.RNA).
        :type strand: coral.DNA or coral.RNA
        :param dotparens: The structure in dotparens notation.
        :type dotparens: str
        :param temp: Temperature setting for the computation. Negative values
                     are not allowed.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :param dangles: How to treat dangles in the computation. From the
                        user guide: For \'none\': Dangle energies are ignored.
                        For \'some\': \'A dangle energy is incorporated for
                        each unpaired base flanking a duplex\'. For 'all': all
                        dangle energy is considered.
        :type dangles: str
        :param sodium: Sodium concentration in solution (molar), only applies
                       to DNA.
        :type sodium: float
        :param magnesium: Magnesium concentration in solution (molar), only
                          applies to DNA>
        :type magnesium: float
        :returns: The free energy of the sequence with the specified secondary
                  structure.
        :rtype: float

        '''
        # Set the material (will be used to set command material flag)
        material = self._set_material(strand, material)

        # Set up command flags
        cmd_args = self._prep_cmd_args(temp, dangles, material, pseudo, sodium,
                                       magnesium, multi=False)

        # Set up the input file and run the command. Note: no STDOUT
        lines = [str(strand), dotparens]
        stdout = self._run('energy', cmd_args, lines).split('\n')

        # Return the energy
        return float(stdout[-2])

Response: Calculate the free energy of a given secondary sequence structure.


Instruction:
Can you generate the documentation for the following Python 3 function
def complexes(self, strands, max_size, ordered=False, pairs=False,
                  mfe=False, cutoff=0.001, degenerate=False, temp=37.0,
                  pseudo=False, material=None, dangles='some', sodium=1.0,
                  magnesium=0.0):
        '''
        :param strands: Strands on which to run energy. Strands must be either
                       coral.DNA or coral.RNA).
        :type strands: list of coral.DNA or coral.RNA
        :param max_size: Maximum complex size to consider (maximum number of
                         strand species in complex).
        :type max_size: int
        :param ordered: Consider distinct ordered complexes - all distinct
                        circular permutations of each complex.
        :type ordered: bool
        :param pairs: Calculate base-pairing observables as with .pairs().
        :type pairs: bool
        :param cutoff: A setting when pairs is set to True - only probabilities
                       above this threshold will be returned.
        :type cutoff: float
        :param degenerate: Applies only when \'mfe\' is set to True. If
                           set to True, the 'mfe' value associated with each
                           complex will be a list of degenerate MFEs (as in
                           the case of .mfe()).
        :type degenerate: bool
        :param temp: Temperature.
        :type temp: float
        :param pseudo: Enable pseudoknots.
        :type pseudo: bool
        :param material: The material setting to use in the computation. If set
                         to None (the default), the material type is inferred
                         from the strands. Other settings available: 'dna' for
                         DNA parameters, 'rna' for RNA (1995) parameters, and
                         'rna1999' for the RNA 1999 parameters.
        :type material: str
        :param dangles: How to treat dangles in the computation. From the
                        user guide: For \'none\': Dangle energies are ignored.
                        For \'some\': \'A dangle energy is incorporated for
                        each unpaired base flanking a duplex\'. For 'all': all
                        dangle energy is considered.
        :type dangles: str
        :param sodium: Sodium concentration in solution (molar), only applies
                       to DNA.
        :type sodium: float
        :param magnesium: Magnesium concentration in solution (molar), only
                          applies to DNA>
        :type magnesium: float
        :returns: A list of dictionaries containing at least 'energy',
                  'complex', and 'strands' keys. If 'ordered' is True, the
                  different possible ordered permutations of complexes are
                  considered. In addition, with 'ordered' set to True, an
                  additional 'order' key describing the exact order of strands
                  and a 'permutation' index (integer) are added. If 'pairs' is
                  True, there is an additional 'epairs' key containing the
                  base-pairing expectation values. If 'mfe' is selected, 'mfe,
                  'dotparens', and 'pairlist' keys in the same as .mfe(). In
                  addition, 'mfe' sets the -ordered flag, so the same keys as
                  when 'ordered' is set to True are added.
        :rtype: float

        '''
        # TODO: Consider returning a pandas dataframe in this (and other)
        # situations to make sorting/selection between results easier.
        material = self._set_material(strands, material, multi=True)
        cmd_args = self._prep_cmd_args(temp, dangles, material, pseudo, sodium,
                                       magnesium, multi=False)
        cmd_args.append('-quiet')
        if mfe:
            cmd_args.append('-mfe')
            ordered = True
            if degenerate:
                cmd_args.append('-degenerate')
        if ordered:
            cmd_args.append('-ordered')
        if pairs:
            cmd_args.append('-pairs')
            cmd_args.append('-cutoff')
            cmd_args.append(cutoff)

        dim = sum([len(s) for s in strands])
        nstrands = len(strands)
        # Set up the input file and run the command
        lines = self._multi_lines(strands, [max_size])
        self._run('complexes', cmd_args, lines)

        # Read the output from file(s)
        if ordered:
            ocx_lines = self._read_tempfile('complexes.ocx').split('\n')
            # Process each lines
            output = []
            for line in ocx_lines:
                if line and not line.startswith('%'):
                    data = line.split('\t')
                    energy = float(data[-1])
                    complexes = [int(d) for d in data[2:2 + nstrands]]
                    permutation = int(data[1])
                    output.append({'energy': energy, 'complex': complexes,
                                   'permutation': permutation})

            key_lines = self._read_tempfile('complexes.ocx-key').split('\n')

            data_lines = [l for l in key_lines if not l.startswith('%')]
            data_lines.pop()
            for i, line in enumerate(data_lines):
                data = line.split('\t')
                keys = [int(d) for d in data[2:-1]]
                output[i]['order'] = keys

            if pairs:
                epairs_data = self._read_tempfile('complexes.ocx-epairs')
                pairslist = self._process_epairs(epairs_data)
                for i, pairs in enumerate(pairslist):
                    output[i]['epairs'] = self._pairs_to_np(pairs, dim)
                # TODO: add ocx-ppairs as well

            if mfe:
                mfe_data = self._read_tempfile('complexes.ocx-mfe')
                if degenerate:
                    return NotImplementedError('Not implemented for complexes')
                else:
                    mfe_output = self._process_mfe(mfe_data, complexes=True)
                    for i, mfedat in enumerate(mfe_output):
                        output[i]['mfe'] = mfedat['mfe']
                        output[i]['dotparens'] = mfedat['dotparens']
                        output[i]['pairlist'] = mfedat['pairlist']
        else:
            cx_lines = self._read_tempfile('complexes.cx').split('\n')
            # Remove empty last line
            cx_lines.pop()
            output = []
            for line in cx_lines:
                if not line.startswith('%'):
                    data = line.split('\t')
                    energy = float(data[-1])
                    complexes = [int(d) for d in data[1:1 + len(strands)]]
                    output.append({'energy': energy, 'complex': complexes})

            if pairs:
                # Process epairs
                epairs_data = self._read_tempfile('complexes.cx-epairs')
                pairslist = self._process_epairs(epairs_data)
                for i, pairs in enumerate(pairslist):
                    proba_mat = self._pairs_to_np(pairs, dim)
                    output[i]['epairs'] = proba_mat

        # Add strands (for downstream concentrations)
        for cx in output:
            cx['strands'] = [s.copy() for s in strands]

        return output

Response: Return a list of complexes for the base - pairing model.


Instruction:
Given the following Python 3 function, write the documentation
def concentrations(self, complexes, concs, ordered=False, pairs=False,
                       cutoff=0.001, temp=37.0):
        '''
        :param complexes: A list of the type returned by the complexes()
                          method.
        :type complexes: list
        :param concs: The concentration(s) of each strand species in the
                      initial complex. If they are all the same, a single
                      float can be used here.
        :type concs: list of floats or float
        :param ordered: Consider distinct ordered complexes - all distinct
                        circular permutations of each complex.
        :type ordered: bool
        :param pairs: Calculate base-pairing observables as with .pairs().
        :type pairs: bool
        :param cutoff: A setting when pairs is set to True - only probabilities
                       above this threshold will be returned.
        :type cutoff: float
        :param temp: Temperature in C.
        :type temp: float
        :returns: A list of dictionaries containing (at least) a
                  'concentrations' key. If 'pairs' is True, an 'fpairs' key
                  is added.
        :rtype: list

        '''
        # Check inputs
        nstrands = len(complexes[0]['strands'])
        try:
            if len(concs) != nstrands:
                raise ValueError('concs argument not same length as strands.')
        except TypeError:
            concs = [concs for i in range(len(complexes['strands']))]

        # Set up command-line arguments
        cmd_args = ['-quiet']
        if ordered:
            cmd_args.append('-ordered')

        # Write .con file
        with open(os.path.join(self._tempdir, 'concentrations.con')) as f:
            f.writelines(concs)

        # Write .cx or .ocx file
        header = ['%t Number of strands: {}'.format(nstrands),
                  '%\tid\tsequence']
        for i, strand in enumerate(complexes['strands']):
            header.append('%\t{}\t{}'.format(i + 1, strand))
        header.append('%\tT = {}'.format(temp))
        body = []
        for i, cx in enumerate(complexes):
            permutation = '\t'.join(complexes['complex'])
            line = '{}\t{}\t{}'.format(i + 1, permutation, complexes['energy'])
            body.append(line)

        if ordered:
            cxfile = os.path.join(self._tempdir, 'concentrations.ocx')
        else:
            cxfile = os.path.join(self._tempdir, 'concentrations.cx')

        with open(cxfile) as f:
            f.writelines(header + body)

        # Run 'concentrations'
        self._run('concentrations', cmd_args, None)

        # Parse the .eq (concentrations) file
        eq_lines = self._read_tempfile('concentrations.eq').split('\n')
        tsv_lines = [l for l in eq_lines if not l.startswith('%')]
        output = []
        for i, line in enumerate(tsv_lines):
            # It's a TSV
            data = line.split('\t')
            # Column 0 is an index
            # Columns 1-nstrands is the complex
            cx = [int(c) for c in data[1:nstrands]]
            # Column nstrands + 1 is the complex energy
            # Column nstrands + 2 is the equilibrium concentration
            eq = float(data[nstrands + 2])
            output[i] = {'complex': cx, 'concentration': eq}

        if pairs:
            # Read the .fpairs file
            pairs = self._read_tempfile('concentrations.fpairs')
            pairs_tsv = [l for l in pairs.split('\n') if not l.startswith('%')]
            # Remove first line (n complexes)
            dim = int(pairs_tsv.pop(0))
            pprob = [[int(p[0]), int(p[1]), float(p[2])] for p in pairs_tsv]
            # Convert to augmented numpy matrix
            fpairs_mat = self.pairs_to_np(pprob, dim)
            for i, out in enumerate(output):
                output[i]['fpairs'] = fpairs_mat

        return output

Response: This method returns a list of dictionaries containing the concentrations of each complex.


Instruction:
Write a Python 3 script for
running the distributions NUPACK command.

Response:
def distributions(self, complexes, counts, volume, maxstates=1e7,
                      ordered=False, temp=37.0):
        '''Runs the \'distributions\' NUPACK command. Note: this is intended
        for a relatively small number of species (on the order of ~20
        total strands for complex size ~14).

        :param complexes: A list of the type returned by the complexes()
                          method.
        :type complexes: list
        :param counts: A list of the exact number of molecules of each initial
                       species (the strands in the complexes command).
        :type counts: list of ints
        :param volume: The volume, in liters, of the container.
        :type volume: float
        :param maxstates: Maximum number of states to be enumerated, needed
                          as allowing too many states can lead to a segfault.
                          In NUPACK, this is referred to as lambda.
        :type maxstates: float
        :param ordered: Consider distinct ordered complexes - all distinct
                        circular permutations of each complex.
        :type ordered: bool
        :param temp: Temperature in C.
        :type temp: float
        :returns: A list of dictionaries containing (at least) a 'complexes'
                  key for the unique complex, an 'ev' key for the expected
                  value of the complex population and a 'probcols' list
                  indicating the probability that a given complex has
                  population 0, 1, ... max(pop) at equilibrium.
        :rtype: list
        :raises: LambdaError if maxstates is exceeded.

        '''
        # Check inputs
        nstrands = len(complexes[0]['strands'])
        if len(counts) != nstrands:
            raise ValueError('counts argument not same length as strands.')

        # Set up command-line arguments
        cmd_args = []
        if ordered:
            cmd_args.append('-ordered')

        # Write .count file
        countpath = os.path.join(self._tempdir, 'distributions.count')
        with open(countpath, 'w') as f:
            f.writelines([str(c) for c in counts] + [str(volume)])

        # Write .cx or .ocx file
        header = ['%t Number of strands: {}'.format(nstrands),
                  '%\tid\tsequence']
        for i, strand in enumerate(complexes['strands']):
            header.append('%\t{}\t{}'.format(i + 1, strand))
        header.append('%\tT = {}'.format(temp))
        body = []
        for i, cx in enumerate(complexes):
            permutation = '\t'.join(complexes['complex'])
            line = '{}\t{}\t{}'.format(i + 1, permutation, complexes['energy'])
            body.append(line)

        if ordered:
            cxfile = os.path.join(self._tempdir, 'distributions.ocx')
        else:
            cxfile = os.path.join(self._tempdir, 'distributions.cx')

        with open(cxfile) as f:
            f.writelines(header + body)

        # Run 'distributions'
        stdout = self._run('distributions', cmd_args, None)

        # Parse STDOUT
        stdout_lines = stdout.split('\n')
        if stdout_lines[0].startswith('Exceeded maximum number'):
            raise LambdaError('Exceeded maxstates combinations.')

        # pop_search = re.search('There are (*) pop', stdout_lines[0]).group(1)
        # populations = int(pop_search)
        # kT_search = re.search('of the box: (*) kT', stdout_lines[1]).group(1)
        # kT = float(kT_search)

        # Parse .dist file (comments header + TSV)
        dist_lines = self._read_tempfile('distributions.dist').split('\n')
        tsv_lines = [l for l in dist_lines if not l.startswith('%')]
        tsv_lines.pop()
        output = []
        for i, line in enumerate(tsv_lines):
            data = line.split('\t')
            # Column 0 is an index
            # Columns 1-nstrands are complexes
            cx = [int(d) for d in data[1:nstrands]]
            # Column nstrands + 1 is expected value of complex
            ev = float(data[nstrands + 1])
            # Columns nstrands + 2 and on are probability columns
            probcols = [float(d) for d in data[nstrands + 2:]]
            output[i]['complex'] = cx
            output[i]['ev'] = ev
            output[i]['probcols'] = probcols

        return output


Instruction:
Create a Python 3 function for
preparing lines to write to file for command input.

Response:
def _multi_lines(self, strands, permutation):
        '''Prepares lines to write to file for pfunc command input.

        :param strand: Strand input (cr.DNA or cr.RNA).
        :type strand: cr.DNA or cr.DNA
        :param permutation: Permutation (e.g. [1, 2, 3, 4]) of the type used
                            by pfunc_multi.
        :type permutation: list
        '''
        lines = []
        # Write the total number of distinct strands
        lines.append(str(len(strands)))
        # Write the distinct strands
        lines += [str(strand) for strand in strands]
        # Write the permutation
        lines.append(' '.join(str(p) for p in permutation))

        return lines


Instruction:
Here you have a function in Python 3, explain what it does
def _read_tempfile(self, filename):
        '''Read in and return file that's in the tempdir.

        :param filename: Name of the file to read.
        :type filename: str

        '''
        with open(os.path.join(self._tempdir, filename)) as f:
            return f.read()

Response: Read in and return file that s in the tempdir.


Instruction:
Create a Python 3 function for
giving a set of pair probability lines construct a numpy array.

Response:
def _pairs_to_np(self, pairlist, dim):
        '''Given a set of pair probability lines, construct a numpy array.

        :param pairlist: a list of pair probability triples
        :type pairlist: list
        :returns: An upper triangular matrix of pair probabilities augmented
                  with one extra column that represents the unpaired
                  probabilities.
        :rtype: numpy.array

        '''
        mat = np.zeros((dim, dim + 1))
        for line in pairlist:
            i = int(line[0]) - 1
            j = int(line[1]) - 1
            prob = float(line[2])
            mat[i, j] = prob
        return mat


Instruction:
Explain what the following Python 3 code does
def _flip_feature(self, feature, parent_len):
    '''Adjust a feature's location when flipping DNA.

    :param feature: The feature to flip.
    :type feature: coral.Feature
    :param parent_len: The length of the sequence to which the feature belongs.
    :type parent_len: int

    '''
    copy = feature.copy()
    # Put on the other strand
    if copy.strand == 0:
        copy.strand = 1
    else:
        copy.strand = 0
    # Adjust locations - guarantee that start is always less than end
    copy.start = parent_len - copy.start
    copy.stop = parent_len - copy.stop
    copy.start, copy.stop = copy.stop, copy.start
    return copy

Response: Flip a feature s location when flipping DNA.


Instruction:
Make a summary of the following Python 3 code
def ape(self, ape_path=None):
        '''Open in ApE if `ApE` is in your command line path.'''
        # TODO: simplify - make ApE look in PATH only
        cmd = 'ApE'
        if ape_path is None:
            # Check for ApE in PATH
            ape_executables = []
            for path in os.environ['PATH'].split(os.pathsep):
                exepath = os.path.join(path, cmd)
                ape_executables.append(os.access(exepath, os.X_OK))
            if not any(ape_executables):
                raise Exception('Ape not in PATH. Use ape_path kwarg.')
        else:
            cmd = ape_path
        # Check whether ApE exists in PATH
        tmp = tempfile.mkdtemp()
        if self.name is not None and self.name:
            filename = os.path.join(tmp, '{}.ape'.format(self.name))
        else:
            filename = os.path.join(tmp, 'tmp.ape')
        coral.seqio.write_dna(self, filename)
        process = subprocess.Popen([cmd, filename])
        # Block until window is closed
        try:
            process.wait()
            shutil.rmtree(tmp)
        except KeyboardInterrupt:
            shutil.rmtree(tmp)

Response: Open in ApE if ApE is in your command line path.


Instruction:
Create a Python 3 function for
creating a safely - editable copy of the current instance.

Response:
def copy(self):
        '''Create a copy of the current instance.

        :returns: A safely-editable copy of the current sequence.
        :rtype: coral.DNA

        '''
        # Significant performance improvements by skipping alphabet check
        features_copy = [feature.copy() for feature in self.features]
        copy = type(self)(self.top.seq, circular=self.circular,
                          features=features_copy, name=self.name,
                          bottom=self.bottom.seq, run_checks=False)
        return copy


Instruction:
How would you explain what the following Python 3 function does
def circularize(self):
        '''Circularize linear DNA.

        :returns: A circularized version of the current sequence.
        :rtype: coral.DNA

        '''
        if self.top[-1].seq == '-' and self.bottom[0].seq == '-':
            raise ValueError('Cannot circularize - termini disconnected.')
        if self.bottom[-1].seq == '-' and self.top[0].seq == '-':
            raise ValueError('Cannot circularize - termini disconnected.')

        copy = self.copy()
        copy.circular = True
        copy.top.circular = True
        copy.bottom.circular = True
        return copy

Response:
Circularize linear DNA.

        :returns: A circularized version of the current sequence.
        :rtype: coral.DNA


Instruction:
Write a Python 3 function for
displaying a visualization of the sequence in an IPython notebook.

Response:
def display(self):
        '''Display a visualization of the sequence in an IPython notebook.'''
        try:
            from IPython.display import HTML
            import uuid
        except ImportError:
            raise IPythonDisplayImportError

        sequence_json = self.json()

        d3cdn = '//d3js.org/d3.v3.min.js'
        div_id = 'sequence_{}'.format(uuid.uuid1())

        cur_dir = os.path.abspath(os.path.dirname(__file__))
        d3_plasmid_path = os.path.join(cur_dir, 'd3-plasmid.js')
        with open(d3_plasmid_path) as f:
            d3_plasmid_js = f.read()

        html = '<div id={div_id}></div>'.format(div_id=div_id)
        js_databind = '''
        <script>
        require([\'{d3_cdn}\'], function(lib) {{
            window.data = {data};'''.format(div_id=div_id, d3_cdn=d3cdn,
                                            data=sequence_json)

        js_viz = '''
            d3sequence(window.data, \'{div_id}\')
        }});
        </script>
        '''.format(div_id=div_id)

        return HTML(html + js_databind + d3_plasmid_js + js_viz)


Instruction:
Make a summary of the following Python 3 code
def excise(self, feature):
        '''Removes feature from circular plasmid and linearizes. Automatically
        reorients at the base just after the feature. This operation is
        complementary to the .extract() method.

        :param feature_name: The feature to remove.
        :type feature_name: coral.Feature

        '''
        rotated = self.rotate_to_feature(feature)
        excised = rotated[feature.stop - feature.start:]

        return excised

Response:
Removes feature from circular plasmid and linearizes. Automatically
        reorients at the base just after the feature.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def extract(self, feature, remove_subfeatures=False):
        '''Extract a feature from the sequence. This operation is complementary
        to the .excise() method.

        :param feature: Feature object.
        :type feature: coral.sequence.Feature
        :param remove_subfeatures: Remove all features in the extracted
                                   sequence aside from the input feature.
        :type remove_subfeatures: bool
        :returns: A subsequence from start to stop of the feature.

        '''
        extracted = self[feature.start:feature.stop]
        # Turn gaps into Ns or Xs
        for gap in feature.gaps:
            for i in range(*gap):
                extracted[i] = self._any_char
        if remove_subfeatures:
            # Keep only the feature specified
            extracted.features = [feature]
        # Update feature locations
        # copy them
        for feature in extracted.features:
            feature.move(-feature.start)
        return extracted

Response: Extract a feature from the sequence. This method is complementary to the. excise method.


Instruction:
Write a Python 3 function for
flipping the top and bottom strands of the DNA.

Response:
def flip(self):
        '''Flip the DNA - swap the top and bottom strands.

        :returns: Flipped DNA (bottom strand is now top strand, etc.).
        :rtype: coral.DNA

        '''
        copy = self.copy()
        copy.top, copy.bottom = copy.bottom, copy.top
        copy.features = [_flip_feature(f, len(self)) for f in copy.features]
        return copy


Instruction:
Make a summary of the following Python 3 code
def locate(self, pattern):
        '''Find sequences matching a pattern. For a circular sequence, the
        search extends over the origin.

        :param pattern: str or NucleicAcidSequence for which to find matches.
        :type pattern: str or coral.DNA
        :returns: A list of top and bottom strand indices of matches.
        :rtype: list of lists of indices (ints)
        :raises: ValueError if the pattern is longer than either the input
                 sequence (for linear DNA) or twice as long as the input
                 sequence (for circular DNA).

        '''
        top_matches = self.top.locate(pattern)
        bottom_matches = self.bottom.locate(pattern)

        return [top_matches, bottom_matches]

Response:
Find sequences matching a pattern. For a linear sequence the
        search extends over the origin. For a circular sequence the
        search extends over the origin.


Instruction:
Implement a function in Python 3 to
rotate the current sequence by n bases.

Response:
def rotate(self, n):
        '''Rotate Sequence by n bases.

        :param n: Number of bases to rotate.
        :type n: int
        :returns: The current sequence reoriented at `index`.
        :rtype: coral.DNA
        :raises: ValueError if applied to linear sequence or `index` is
                 negative.

        '''
        if not self.circular and n != 0:
            raise ValueError('Cannot rotate linear DNA')
        else:
            copy = self.copy()
            copy.top = self.top.rotate(n)
            copy.bottom = self.bottom.rotate(-n)
            copy.features = []
            for feature in self.features:
                feature_copy = feature.copy()
                feature_copy.move(n)
                # Adjust the start/stop if we move over the origin
                feature_copy.start = feature_copy.start % len(self)
                feature_copy.stop = feature_copy.stop % len(self)
                copy.features.append(feature_copy)

            return copy.circularize()


Instruction:
How would you implement a function in Python 3 that
reverses complement the current sequence.

Response:
def reverse_complement(self):
        '''Reverse complement the DNA.

        :returns: A reverse-complemented instance of the current sequence.
        :rtype: coral.DNA

        '''
        copy = self.copy()
        # Note: if sequence is double-stranded, swapping strand is basically
        # (but not entirely) the same thing - gaps affect accuracy.
        copy.top = self.top.reverse_complement()
        copy.bottom = self.bottom.reverse_complement()

        # Remove all features - the reverse complement isn't flip!
        copy.features = []

        return copy


Instruction:
How would you explain what the following Python 3 function does
def select_features(self, term, by='name', fuzzy=False):
        '''Select features from the features list based on feature name,
           gene, or locus tag.
           :param term: Search term.
           :type term: str
           :param by: Feature attribute to search by. Options are 'name',
           'gene', and 'locus_tag'.
           :type by: str
           :param fuzzy: If True, search becomes case-insensitive and will also
           find substrings - e.g. if fuzzy search is enabled, a search for
           'gfp' would return a hit for a feature named 'GFP_seq'.
           :type fuzzy: bool
           :returns: A list of features matched by the search.
           :rtype: list

        '''
        features = []
        if fuzzy:
            fuzzy_term = term.lower()
            for feature in self.features:
                if fuzzy_term in feature.__getattribute__(by).lower():
                    features.append(feature)
        else:
            for feature in self.features:
                if feature.__getattribute__(by) == term:
                    features.append(feature)

        return features

Response: Select features from the features list based on a search term.


Instruction:
Create a Python 3 function to
create a new feature from the current object.

Response:
def to_feature(self, name=None, feature_type='misc_feature'):
        '''Create a feature from the current object.

        :param name: Name for the new feature. Must be specified if the DNA
                     instance has no .name attribute.
        :type name: str
        :param feature_type: The type of feature (genbank standard).
        :type feature_type: str

        '''
        if name is None:
            if not self.name:
                raise ValueError('name attribute missing from DNA instance'
                                 ' and arguments')
            name = self.name
        return Feature(name, start=0, stop=len(self),
                       feature_type=feature_type)


Instruction:
How would you code a function in Python 3 to
report whether the enzyme cuts outside its recognition site.

Response:
def cuts_outside(self):
        '''Report whether the enzyme cuts outside its recognition site.
        Cutting at the very end of the site returns True.

        :returns: Whether the enzyme will cut outside its recognition site.
        :rtype: bool

        '''
        for index in self.cut_site:
            if index < 0 or index > len(self.recognition_site) + 1:
                return True
        return False


Instruction:
Can you generate a brief explanation for the following Python 3 code
def copy(self):
        '''Generate a Primer copy.

        :returns: A safely-editable copy of the current primer.
        :rtype: coral.DNA

        '''
        return type(self)(self.anneal, self.tm, overhang=self.overhang,
                          name=self.name, note=self.note)

Response: Generate a safely - editable copy of the current primer.


Instruction:
Can you write a function in Python 3 where it
calculates nearest - neighbor melting temperature in °C.

Response:
def tm(seq, dna_conc=50, salt_conc=50, parameters='cloning'):
    '''Calculate nearest-neighbor melting temperature (Tm).

    :param seq: Sequence for which to calculate the tm.
    :type seq: coral.DNA
    :param dna_conc: DNA concentration in nM.
    :type dna_conc: float
    :param salt_conc: Salt concentration in mM.
    :type salt_conc: float
    :param parameters: Nearest-neighbor parameter set. Available options:
                       'breslauer': Breslauer86 parameters
                       'sugimoto': Sugimoto96 parameters
                       'santalucia96': SantaLucia96 parameters
                       'santalucia98': SantaLucia98 parameters
                       'cloning': breslauer without corrections
                       'cloning_sl98': santalucia98 fit to 'cloning'
    :type parameters: str
    :returns: Melting temperature (Tm) in °C.
    :rtype: float
    :raises: ValueError if parameter argument is invalid.

    '''
    if parameters == 'breslauer':
        params = tm_params.BRESLAUER
    elif parameters == 'sugimoto':
        params = tm_params.SUGIMOTO
    elif parameters == 'santalucia96':
        params = tm_params.SANTALUCIA96
    elif parameters == 'santalucia98' or parameters == 'cloning_sl98':
        params = tm_params.SANTALUCIA98
    elif parameters == 'cloning':
        params = tm_params.CLONING
    else:
        raise ValueError('Unsupported parameter set.')

    # Thermodynamic parameters
    pars = {'delta_h': params['delta_h'], 'delta_s': params['delta_s']}
    pars_error = {'delta_h': params['delta_h_err'],
                  'delta_s': params['delta_s_err']}

    # Error corrections - done first for use of reverse_complement parameters
    if parameters == 'breslauer':
        deltas = breslauer_corrections(seq, pars_error)
    elif parameters == 'sugimoto':
        deltas = breslauer_corrections(seq, pars_error)
    elif parameters == 'santalucia96':
        deltas = breslauer_corrections(seq, pars_error)
    elif parameters == 'santalucia98' or parameters == 'cloning_sl98':
        deltas = santalucia98_corrections(seq, pars_error)
    elif parameters == 'cloning':
        deltas = breslauer_corrections(seq, pars_error)
        deltas[0] += 3.4
        deltas[1] += 12.4

    # Sum up the nearest-neighbor enthalpy and entropy
    seq = str(seq).upper()

    # TODO: catch more cases when alphabets expand
    if 'N' in seq:
        raise ValueError('Can\'t calculate Tm of an N base.')
    new_delt = _pair_deltas(seq, pars)
    deltas[0] += new_delt[0]
    deltas[1] += new_delt[1]

    # Unit corrections
    salt_conc /= 1e3
    dna_conc /= 1e9
    deltas[0] *= 1e3

    # Universal gas constant (R)
    R = 1.9872

    # Supposedly this is what dnamate does, but the output doesn't match theirs
#    melt = (-deltas[0] / (-deltas[1] + R * log(dna_conc / 4.0))) +
#                          16.6 * log(salt_conc) - 273.15
#    return melt
    # Overall equation is supposedly:
    # sum{dH}/(sum{dS} + R ln(dna_conc/b)) - 273.15
    # with salt corrections for the whole term (or for santalucia98,
    # salt corrections added to the dS term.
    # So far, implementing this as described does not give results that match
    # any calculator but Biopython's

    if parameters == 'breslauer' or parameters == 'cloning':
        numerator = -deltas[0]
        # Modified dna_conc denominator
        denominator = (-deltas[1]) + R * log(dna_conc / 16.0)
        # Modified Schildkraut-Lifson equation adjustment
        salt_adjustment = 16.6 * log(salt_conc) / log(10.0)
        melt = numerator / denominator + salt_adjustment - 273.15
    elif parameters == 'santalucia98' or 'cloning_sl98':
        # TODO: dna_conc should be divided by 2.0 when dna_conc >> template
        # (like PCR)
        numerator = -deltas[0]
        # SantaLucia 98 salt correction
        salt_adjustment = 0.368 * (len(seq) - 1) * log(salt_conc)
        denominator = -deltas[1] + salt_adjustment + R * log(dna_conc / 4.0)
        melt = -deltas[0] / denominator - 273.15
    elif parameters == 'santalucia96':
        # TODO: find a way to test whether the code below matches another
        # algorithm. It appears to be correct, but need to test it.
        numerator = -deltas[0]
        denominator = -deltas[1] + R * log(dna_conc / 4.0)
        # SantaLucia 96 salt correction
        salt_adjustment = 12.5 * log10(salt_conc)
        melt = numerator / denominator + salt_adjustment - 273.15
    elif parameters == 'sugimoto':
        # TODO: the stuff below is untested and probably wrong
        numerator = -deltas[0]
        denominator = -deltas[1] + R * log(dna_conc / 4.0)
        # Sugimoto parameters were fit holding salt concentration constant
        # Salt correction can be chosen / ignored? Remove sugimoto set since
        # it's so similar to santalucia98?
        salt_correction = 16.6 * log10(salt_conc)
        melt = numerator / denominator + salt_correction - 273.15

    if parameters == 'cloning_sl98':
        # Corrections to make santalucia98 method approximate cloning method.
        # May be even better for cloning with Phusion than 'cloning' method
        melt *= 1.27329212575
        melt += -2.55585450119

    return melt


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _pair_deltas(seq, pars):
    '''Add up nearest-neighbor parameters for a given sequence.

    :param seq: DNA sequence for which to sum nearest neighbors
    :type seq: str
    :param pars: parameter set to use
    :type pars: dict
    :returns: nearest-neighbor delta_H and delta_S sums.
    :rtype: tuple of floats

    '''
    delta0 = 0
    delta1 = 0
    for i in range(len(seq) - 1):
        curchar = seq[i:i + 2]
        delta0 += pars['delta_h'][curchar]
        delta1 += pars['delta_s'][curchar]
    return delta0, delta1

Response: Add up nearest - neighbor parameters for a given DNA sequence.


Instruction:
How would you code a function in Python 3 to
sum corrections for Breslauer 84 method.

Response:
def breslauer_corrections(seq, pars_error):
    '''Sum corrections for Breslauer '84 method.

    :param seq: sequence for which to calculate corrections.
    :type seq: str
    :param pars_error: dictionary of error corrections
    :type pars_error: dict
    :returns: Corrected delta_H and delta_S parameters
    :rtype: list of floats

    '''
    deltas_corr = [0, 0]
    contains_gc = 'G' in str(seq) or 'C' in str(seq)
    only_at = str(seq).count('A') + str(seq).count('T') == len(seq)
    symmetric = seq == seq.reverse_complement()
    terminal_t = str(seq)[0] == 'T' + str(seq)[-1] == 'T'

    for i, delta in enumerate(['delta_h', 'delta_s']):
        if contains_gc:
            deltas_corr[i] += pars_error[delta]['anyGC']
        if only_at:
            deltas_corr[i] += pars_error[delta]['onlyAT']
        if symmetric:
            deltas_corr[i] += pars_error[delta]['symmetry']
        if terminal_t and delta == 'delta_h':
            deltas_corr[i] += pars_error[delta]['terminalT'] * terminal_t
    return deltas_corr


Instruction:
Write a Python 3 script for
summing corrections for Santa Lucia 9 method.

Response:
def santalucia98_corrections(seq, pars_error):
    '''Sum corrections for SantaLucia '98 method (unified parameters).

    :param seq: sequence for which to calculate corrections.
    :type seq: str
    :param pars_error: dictionary of error corrections
    :type pars_error: dict
    :returns: Corrected delta_H and delta_S parameters
    :rtype: list of floats

    '''
    deltas_corr = [0, 0]
    first = str(seq)[0]
    last = str(seq)[-1]

    start_gc = first == 'G' or first == 'C'
    start_at = first == 'A' or first == 'T'
    end_gc = last == 'G' or last == 'C'
    end_at = last == 'A' or last == 'T'
    init_gc = start_gc + end_gc
    init_at = start_at + end_at

    symmetric = seq == seq.reverse_complement()

    for i, delta in enumerate(['delta_h', 'delta_s']):
        deltas_corr[i] += init_gc * pars_error[delta]['initGC']
        deltas_corr[i] += init_at * pars_error[delta]['initAT']
        if symmetric:
            deltas_corr[i] += pars_error[delta]['symmetry']
    return deltas_corr


Instruction:
Can you tell what is the following Python 3 function doing
def repeats(seq, size):
    '''Count times that a sequence of a certain size is repeated.

    :param seq: Input sequence.
    :type seq: coral.DNA or coral.RNA
    :param size: Size of the repeat to count.
    :type size: int
    :returns: Occurrences of repeats and how many
    :rtype: tuple of the matched sequence and how many times it occurs

    '''
    seq = str(seq)
    n_mers = [seq[i:i + size] for i in range(len(seq) - size + 1)]
    counted = Counter(n_mers)
    # No one cares about patterns that appear once, so exclude them
    found_repeats = [(key, value) for key, value in counted.iteritems() if
                     value > 1]
    return found_repeats

Response: Count times that a sequence of a certain size is repeated.


Instruction:
Write a Python 3 function for
simulating a Gibson reaction.

Response:
def gibson(seq_list, linear=False, homology=10, tm=63.0):
    '''Simulate a Gibson reaction.

    :param seq_list: list of DNA sequences to Gibson
    :type seq_list: list of coral.DNA
    :param linear: Attempt to produce linear, rather than circular,
                   fragment from input fragments.
    :type linear: bool
    :param homology_min: minimum bp of homology allowed
    :type homology_min: int
    :param tm: Minimum tm of overlaps
    :type tm: float
    :returns: coral.reaction.Gibson instance.
    :raises: ValueError if any input sequences are circular DNA.

    '''
    # FIXME: Preserve features in overlap
    # TODO: set a max length?
    # TODO: add 'expected' keyword argument somewhere to automate
    # validation

    # Remove any redundant (identical) sequences
    seq_list = list(set(seq_list))
    for seq in seq_list:
        if seq.circular:
            raise ValueError('Input sequences must be linear.')

    # Copy input list
    working_list = [s.copy() for s in seq_list]

    # Attempt to fuse fragments together until only one is left
    while len(working_list) > 1:
        working_list = _find_fuse_next(working_list, homology, tm)
    if not linear:
        # Fuse the final fragment to itself
        working_list = _fuse_last(working_list, homology, tm)

    # Clear features
    working_list[0].features = []
    return _annotate_features(working_list[0], seq_list)


Instruction:
Make a summary of the following Python 3 code
def _find_fuse_next(working_list, homology, tm):
    '''Find the next sequence to fuse, and fuse it (or raise exception).

    :param homology: length of terminal homology in bp
    :type homology: int
    :raises: AmbiguousGibsonError if there is more than one way for the
             fragment ends to combine.
             GibsonOverlapError if no homology match can be found.

    '''
    # 1. Take the first sequence and find all matches
    # Get graphs:
    #   a) pattern watson : targets watson
    #   b) pattern watson : targets crick
    #   c) pattern crick: targets watson
    #   d) pattern crick: targets crick
    pattern = working_list[0]
    targets = working_list[1:]

    # Output graph nodes of terminal binders:
    #   (destination, size, strand1, strand2)
    def graph_strands(strand1, strand2):
        graph = []
        for i, target in enumerate(targets):
            matchlen = homology_report(pattern, target, strand1, strand2,
                                       cutoff=homology, min_tm=tm)
            if matchlen:
                graph.append((i, matchlen, strand1, strand2))
        return graph

    graph_ww = graph_strands('w', 'w')
    graph_wc = graph_strands('w', 'c')
    graph_cw = graph_strands('c', 'w')
    graph_cc = graph_strands('c', 'c')
    graphs_w = graph_ww + graph_wc
    graphs_c = graph_cw + graph_cc
    graphs = graphs_w + graphs_c

    # 2. See if there's more than one result on a strand.
    # If so, throw an exception.
    if len(graphs_w) > 1 or len(graphs_c) > 1:
        raise AmbiguousGibsonError('multiple compatible ends.')
    if len(graphs_w) == len(graphs_c) == 0:
        raise GibsonOverlapError('Failed to find compatible Gibson ends.')

    # 3. There must be one result. Where is it?
    # If there's one result on each strand, go with the one that matches the
    # pattern watson strand (will occur first - index 0)
    match = graphs[0]

    # 4. Combine pieces together
    # 4a. Orient pattern sequence
    if match[2] == 'c':
        left_side = pattern.reverse_complement()
    else:
        left_side = pattern
    # 4b. Orient target sequence
    if match[3] == 'w':
        right_side = working_list.pop(match[0] + 1).reverse_complement()
    else:
        right_side = working_list.pop(match[0] + 1)

    working_list[0] = left_side + right_side[match[1]:]
    return working_list

Response: Find the next sequence to fuse and fuse it.


Instruction:
Can you implement a function in Python 3 that
fuses the last termini in a list of working_list.

Response:
def _fuse_last(working_list, homology, tm):
    '''With one sequence left, attempt to fuse it to itself.

    :param homology: length of terminal homology in bp.
    :type homology: int
    :raises: AmbiguousGibsonError if either of the termini are palindromic
             (would bind self-self).
             ValueError if the ends are not compatible.

    '''
    # 1. Construct graph on self-self
    #    (destination, size, strand1, strand2)
    pattern = working_list[0]

    def graph_strands(strand1, strand2):
        matchlen = homology_report(pattern, pattern, strand1, strand2,
                                   cutoff=homology, min_tm=tm, top_two=True)
        if matchlen:
            # Ignore full-sequence matches
            # HACK: modified homology_report to accept top_two. It should
            # really just ignore full-length matches
            for length in matchlen:
                if length != len(pattern):
                    return (0, length, strand1, strand2)
        else:
            return []

    # cw is redundant with wc
    graph_ww = graph_strands('w', 'w')
    graph_wc = graph_strands('w', 'c')
    graph_cc = graph_strands('c', 'c')
    if graph_ww + graph_cc:
        raise AmbiguousGibsonError('Self-self binding during circularization.')
    if not graph_wc:
        raise ValueError('Failed to find compatible ends for circularization.')

    working_list[0] = working_list[0][:-graph_wc[1]].circularize()

    return working_list