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)
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))
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")