Example #1
0
    def _populate_model_from_tbn_and_polymer_basis(self, tbn: Tbn,
                                                   basis: np.array) -> None:
        self.polymer_basis = basis

        # upper bound on how many total monomers can be in non-singleton polymers
        upper_bound_on_total_monomers_in_complexes = sum(
            tbn.count(monomer_type) *
            (1 + abs(monomer_type.net_count(domain_type)))
            for monomer_type in tbn.limiting_monomer_types()
            for domain_type in tbn.limiting_domain_types())
        monomer_counts = [
            tbn.count(monomer) for monomer in tbn.monomer_types()
        ]
        upper_bound_on_total_monomers_in_complexes = min(
            upper_bound_on_total_monomers_in_complexes,
            sum(monomer_counts)  #total number of monomers
        )

        BIG_M = upper_bound_on_total_monomers_in_complexes

        self.model.set_big_m(BIG_M)

        # declare variables
        size_of_basis_vectors, number_of_basis_vectors = basis.shape
        self.basis_coefficients = [
            self.model.int_var(0, BIG_M, f"basis_coefficient_{i}")
            for i in range(number_of_basis_vectors)
        ]

        # conservation constraints
        for i, monomer_type in enumerate(list(self.tbn.monomer_types())):
            self.model.Add(
                self.tbn.count(monomer_type) == sum(
                    self.basis_coefficients[j] * basis_vector[i]
                    for j, basis_vector in enumerate(basis.T)))

        # counting constraints
        number_of_polymers = sum(self.basis_coefficients)

        if self.user_constraints.max_polymers() != infinity:
            self.model.add_constraint(
                number_of_polymers <= self.user_constraints.max_polymers())
        if self.user_constraints.min_polymers() > 0:
            self.model.add_constraint(
                number_of_polymers >= self.user_constraints.min_polymers())

        # objective function
        if self.user_constraints.optimize():
            self.model.maximize(number_of_polymers)
Example #2
0
 def test_monomer_types(self):
     tests = [
         ({}, []),
         ({self.x: 3}, [self.x]),
         ({self.y: 5}, [self.y]),
         ({self.x: 5, self.y: 2}, [self.x, self.y]),
         ({self.x: infinity, self.y: infinity}, [self.x, self.y]),
     ]
     for monomer_multiset, monomer_types in tests:
         tbn = Tbn(monomer_multiset)
         with self.subTest("ordinary monomer type iterator", tbn=tbn):
             self.assertEqual(monomer_types, list(tbn.monomer_types()))
     flatten_tests = [
         ({}, []),
         ({self.x: 3}, [self.x]),
         ({self.y: 5}, [self.y]),
         ({self.x: 5, self.y: 2}, [self.x, self.y]),
     ]
     for monomer_multiset, monomer_types in flatten_tests:
         tbn = Tbn(monomer_multiset)
         with self.subTest("monomer type iterator with flatten", tbn=tbn):
             flattened_list = list(tbn.monomer_types(flatten=True))
             for monomer_type in monomer_types:
                 self.assertEqual(tbn.count(monomer_type), flattened_list.count(monomer_type))
Example #3
0
    def _get_hilbert_basis_from_matrix(matrix: np.array,
                                       tbn: Tbn = None,
                                       quiet: bool = False) -> np.array:
        # if a TBN is specified, the Hilbert basis will be constrained to the quantities of the monomers in the TBN
        temporary_filename_prefix = os.path.join(
            os.getcwd(), f"TEMP_4ti2_CALLOUT_{randint(0,10000)}")
        matrix_filename = temporary_filename_prefix + '.mat'
        relations_filename = temporary_filename_prefix + '.rel'
        signs_filename = temporary_filename_prefix + '.sign'
        rhs_filename = temporary_filename_prefix + '.rhs'
        homogenous_basis_filename = temporary_filename_prefix + '.zhom'
        inhomogenous_basis_filename = temporary_filename_prefix + '.zinhom'

        number_of_domain_types = matrix.shape[0]
        number_of_monomer_types = matrix.shape[1]

        if tbn:
            identity_matrix = np.identity(number_of_monomer_types, np.int64)
            matrix = np.concatenate((matrix, identity_matrix))

        # write a temporary matrix file
        with open(matrix_filename, 'w') as outFile:
            # shape is first line of the .mat file
            number_of_rows = number_of_domain_types + (number_of_monomer_types
                                                       if tbn else 0)
            outFile.write(f"{number_of_rows} {number_of_monomer_types}\n")
            for row in matrix:
                for entry in row:
                    outFile.write(f"{entry} ")
                outFile.write('\n')

        # write a temporary relations file
        with open(relations_filename, 'w') as outFile:
            entries = number_of_domain_types + (number_of_monomer_types
                                                if tbn else 0)
            outFile.write(f"1 {entries}\n")
            outFile.write(' '.join(['>'] * number_of_domain_types))
            if tbn:
                outFile.write(' ')
                outFile.write(' '.join(['<'] * number_of_monomer_types))
            outFile.write('\n')

        # write a temporary signs file
        with open(signs_filename, 'w') as outFile:
            outFile.write(f"1 {number_of_monomer_types}\n")
            outFile.write(' '.join(['1'] * number_of_monomer_types))
            outFile.write('\n')

        # write a temporary right hand sides file
        with open(rhs_filename, 'w') as outFile:
            entries = number_of_domain_types + (number_of_monomer_types
                                                if tbn else 0)
            outFile.write(f"1 {entries}\n")
            outFile.write(' '.join(['0'] * number_of_domain_types))
            if tbn:
                outFile.write(' ')
                outFile.write(' '.join([
                    str(tbn.count(monomer)) for monomer in tbn.monomer_types()
                ]))
            outFile.write('\n')

        try:
            subprocess.call(["4ti2-zsolve", temporary_filename_prefix] +
                            (['-q'] if quiet else []))

            # read and interpret response
            with open(homogenous_basis_filename, 'r') as inFile:
                # shape is first line of the file
                shape_as_string = inFile.readline()
                shape = tuple(int(x) for x in shape_as_string.split())

                flat_array = np.array(inFile.read().split(), np.int64)
                homogenous_basis = flat_array.reshape(*shape)

            with open(inhomogenous_basis_filename, 'r') as inFile:
                # shape is first line of the file
                shape_as_string = inFile.readline()
                shape = tuple(int(x) for x in shape_as_string.split())

                flat_array = np.array(inFile.read().split(), np.int64)
                inhomogenous_basis = flat_array.reshape(*shape)

                # remove the row of all zeros
                inhomogenous_basis = inhomogenous_basis[
                    ~np.all(inhomogenous_basis == 0, axis=1)]

            if homogenous_basis.size == 0:
                basis = inhomogenous_basis.T
            elif inhomogenous_basis.size == 0:
                basis = homogenous_basis.T
            else:
                basis = np.concatenate(homogenous_basis, inhomogenous_basis).T

            print('=' * 80)
            print(homogenous_basis)
            print('-' * 80)
            print(inhomogenous_basis)
            print('-' * 80)
            print(basis)
            print('=' * 80)
        except FileNotFoundError:
            print("4ti2 was not able to complete")
            basis = None
        finally:
            # clean up
            for filename in [
                    matrix_filename,
                    relations_filename,
                    signs_filename,
                    rhs_filename,
                    inhomogenous_basis_filename,
                    homogenous_basis_filename,
            ]:
                try:
                    os.remove(filename)
                except FileNotFoundError:
                    pass
        if basis.size > 0:
            return basis
        else:
            raise AssertionError(
                "the callout to 4ti2 did not generate a Hilbert basis")