def _project_tbn_to_column_matrix(tbn: Tbn) -> np.array: monomer_types = list(tbn.monomer_types()) limiting_domain_types = list(tbn.limiting_domain_types()) monomer_matrix = np.array( [[-monomer.net_count(domain) for domain in limiting_domain_types] for monomer in monomer_types], np.int64).T return monomer_matrix
def test_from_string(self): with self.subTest("single monomer example"): self.assertEqual(Tbn({Monomer.from_string("a"): 1}), Tbn.from_string("a")) with self.subTest("single monomer type, multiple monomer example"): self.assertEqual(Tbn({Monomer.from_string("a"): 2}), Tbn.from_string("2[a]")) with self.subTest("testing from_string with example from stablegen.net/help"): example_text = \ """ a*:b1 b* a b:b2 >m1 a* >m2 b* """ example_tbn = Tbn({ Monomer.from_string("a* b*"): 1, Monomer.from_string("a b", "m1"): 1, Monomer.from_string("a*", "m2"): 1, Monomer.from_string("b*"): 1, }) self.assertEqual(example_tbn, Tbn.from_string(example_text)) with self.subTest("testing from_string with multisets"): multiset_text = \ """ 2[ 3(a*) a b ] [ c ] 5[ a a:favorite_a b >bob ] 7[ b a b b ] b a b b b* """ multiset_tbn = Tbn({ Monomer.from_string("a* a* a* a b"): 2, Monomer.from_string("c"): 1, Monomer.from_string("2(a) b", "bob"): 5, Monomer.from_string("3(b) a"): 7, Monomer.from_string("b* 3(b) a"): 1, }) self.assertEqual(multiset_tbn, Tbn.from_string(multiset_text)) with self.subTest("testing from_string with excess monomers"): multiset_text = \ """ 2[ 3(a*) a b ] inf[ c ] 5[ a a b >bob ] inf[ b a b b ] b a b b b* """ multiset_tbn = Tbn({ Monomer.from_string("a* a* a* a b"): 2, Monomer.from_string("c"): infinity, Monomer.from_string("2(a) b", "bob"): 5, Monomer.from_string("3(b) a"): infinity, Monomer.from_string("b* 3(b) a"): 1, }) self.assertEqual(multiset_tbn, Tbn.from_string(multiset_text))
def test_init(self): for quantity in [0, -1, -2, 'a', '^', '-inf']: with self.subTest("Do not allow nonpositive monomer quantities", quantity=quantity): with self.assertRaises(AssertionError): Tbn({self.x: 2, self.y: quantity}) with self.subTest("allow infinite monomer quantities"): Tbn({self.x: infinity, self.y: 2}) Tbn({self.x: 2, self.y: infinity})
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_limiting_monomer_types(self): test_tbn = Tbn({ Monomer.from_string("a b c e"): infinity, Monomer.from_string("a d*"): 1, Monomer.from_string("d*"): infinity, Monomer.from_string("a*"): 2, # a* is limiting in this example Monomer.from_string("a d e*"): 3, # both d and e* are limiting in this example Monomer.from_string("f"): 1, Monomer.from_string("f*"): 1, # f* is chosen as limiting to break the tie (favors stars) }) limiting_monomer_types = sorted([ Monomer.from_string("a d e*"), Monomer.from_string("a*"), Monomer.from_string("f*") ]) self.assertEqual(limiting_monomer_types, list(test_tbn.limiting_monomer_types()))
def flatten(self) -> Tbn: monomer_counts = {} for polymer, polymer_count in self.__polymer_counts.items(): for monomer, monomer_count in polymer.items(): monomer_counts[monomer] = (polymer_count * monomer_count) + monomer_counts.get( monomer, 0) return Tbn(monomer_counts)
def test_configs_with_number_of_polymers(self): test_cases = [ # second argument is a list of number of configurations expected for specific numbers of polymers: # e.g. [a,b,c,d] => 'a' configurations with 1 polymer, 'b' configurations with 2 polymers, etc., up to 4 ("a* b* \n a b \n a* \n b*", [1, 4, 1, 0], self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("a* b* \n a b \n a* \n b*", [1, 4, 1, 0], self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("a* b* \n a b \n a* \n b*", [1, 4, 1, 0], self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), # recall that POLYMER_UNBOUNDED_MATRIX does not allow spurious binding of polymers without limiting monomers ("a* b* \n a b \n a* \n b*", [1, 3, 1, 0], self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), # here VARIABLE_BOND_WEIGHT does not require saturation, hence more configurations ("a* b* \n a b \n a* \n b*", [1, 3, 3, 1], self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("2[a* b*] \n a b", [1, 2, 0, 0], self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("2[a* b*] \n a b", [1, 2, 0, 0], self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("2[a* b*] \n a b", [1, 1, 0, 0], self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("2[a* b*] \n a b", [1, 1, 0, 0], self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("2[a* b*] \n a b", [1, 1, 1, 0], self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", [1, 4, 0, 0], self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", [1, 4, 0, 0], self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", [1, 3, 0, 0], self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", [1, 3, 0, 0], self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ] for tbn_string, number_of_configs_with_polymer_count, solver, formulation in test_cases: with self.subTest(tbn_string=tbn_string, solver=solver, formulation=formulation): test_tbn = Tbn.from_string(tbn_string) for polymer_count_minus_one, number_of_configs in enumerate( number_of_configs_with_polymer_count): polymer_count = polymer_count_minus_one + 1 constraints = Constraints().with_fixed_polymers( polymer_count).with_unset_optimization_flag() configurations = list( solver.stable_configs(test_tbn, constraints, formulation=formulation)) self.assertEqual(number_of_configs, len(configurations)) for configuration in configurations: self.assertEqual(polymer_count, configuration.number_of_polymers())
def test_limiting_domain_types(self): tests = [ ({}, []), ({self.x: 3}, [Domain("x0*"), Domain("x1*")]), ({self.y: 5}, [Domain("y0*"), Domain("y1*"), Domain("y2*")]), ({self.x: 5, self.y: 2}, [Domain("x0*"), Domain("x1*"), Domain("y0*"), Domain("y1*"), Domain("y2*")]), ({Monomer.from_string("a*"): 2, Monomer.from_string("a"): 1}, [Domain("a")]), ({Monomer.from_string("3(a*)"): 1, Monomer.from_string("a"): 2}, [Domain("a")]), ({Monomer.from_string("a*"): 1, Monomer.from_string("a"): 2}, [Domain("a*")]), ({Monomer.from_string("2(a*)"): 1, Monomer.from_string("a"): 2}, [Domain("a*")]), ({Monomer.from_string("2(a*)"): 1, Monomer.from_string("a"): infinity}, [Domain("a*")]), ({Monomer.from_string("2(a*)"): infinity, Monomer.from_string("a"): 2}, [Domain("a")]), ] for monomer_multiset, expected_limiting_domain_types in tests: with self.subTest("limiting domain types", tbn=str(Tbn(monomer_multiset))): limiting_domain_types = list(Tbn(monomer_multiset).limiting_domain_types()) self.assertEqual(expected_limiting_domain_types, limiting_domain_types) with self.subTest("cannot have conflicting excess domain types"): conflicting_excess_tbn = Tbn( {Monomer.from_string("a"): infinity, Monomer.from_string("a*"): infinity} ) with self.assertRaises(AssertionError): list(conflicting_excess_tbn.limiting_domain_types()) with self.subTest("test equal count tie-breaking filter"): monomer_multiset = { Monomer.from_string("2(a)"): 1, Monomer.from_string("a*"): 2, Monomer.from_string("b*"): 1 } limiting_domain_types = list(Tbn(monomer_multiset).limiting_domain_types(filter_ties=True)) self.assertEqual([Domain("b")], limiting_domain_types)
def setUp(self): self.x = Monomer.from_string("x0 x1", "X") self.y = Monomer.from_string("2(y0) 1(y1) 3(y2)", "Y") self.Tbn_1x = Tbn({self.x: 1}) self.Tbn_1y = Tbn({self.y: 1}) self.Tbn_1x_1y = Tbn({self.y: 1, self.x: 1}) self.Tbn_2x_3y = Tbn({self.y: 3, self.x: 2}) self.Tbn_infx_2y = Tbn({self.x: infinity, self.y: 2}) self.Tbn_2x_infy = Tbn({self.y: infinity, self.x: 2}) self.Tbn_1x_infy = Tbn({self.y: infinity, self.x: 1})
def test_project_tbn_to_column_matrix(self): with self.subTest("slack version"): test_tbn = Tbn.from_string("a* b* \n a b \n a* \n b*") actual_matrix = Formulation._project_tbn_to_column_matrix(test_tbn) expected_matrix = np.array( [ [-1, -1], # a b [1, 1], # a* b* [1, 0], # a* [0, 1], # b* ], np.int64).T self.assertTrue(np.array_equal(expected_matrix, actual_matrix)) with self.subTest("no slack version"): test_tbn = Tbn.from_string( "a* b* \n a b" ) # no slack this time, should default to stars being limiting actual_matrix = Formulation._project_tbn_to_column_matrix(test_tbn) expected_matrix = np.array( [ [1, 1], # a b [-1, -1], # a* b* ], np.int64).T self.assertTrue(np.array_equal(expected_matrix, actual_matrix)) with self.subTest("second no slack version"): test_tbn = Tbn.from_string("a \n a* b* \n a b") actual_matrix = Formulation._project_tbn_to_column_matrix(test_tbn) expected_matrix = np.array( [ [1, 1], # a b [-1, -1], # a* b* [1, 0], # a ], np.int64).T self.assertTrue(np.array_equal(expected_matrix, actual_matrix))
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 test_eq(self): self.assertEqual(Tbn({Monomer.from_string("a"): 1}), Tbn({Monomer.from_string("a"): 1})) self.assertEqual(Tbn({Monomer.from_string("a"): infinity}), Tbn({Monomer.from_string("a"): infinity})) self.assertNotEqual(Tbn({Monomer.from_string("a"): 1}), Tbn({Monomer.from_string("a"): 2})) self.assertNotEqual(Tbn({Monomer.from_string("a"): 1}), Tbn({Monomer.from_string("b"): 1})) self.assertNotEqual(Tbn({Monomer.from_string("a"): 1}), Tbn({Monomer.from_string("a"): infinity}))
def test_flatten(self): self.assertEqual(Tbn({}), Configuration({}).flatten()) self.assertEqual(Tbn({ self.x: 1, self.y: 1 }), Configuration({ self.polymer_1x_1y: 1 }).flatten()) self.assertEqual(Tbn({ self.x: 2, self.y: 3 }), Configuration({ self.polymer_2x_3y: 1 }).flatten()) self.assertEqual( Tbn({ self.x: 3, self.y: 4 }), Configuration({ self.polymer_1x_1y: 1, self.polymer_2x_3y: 1 }).flatten()) self.assertEqual( Tbn({ self.x: 5, self.y: 7 }), Configuration({ self.polymer_1x_1y: 1, self.polymer_2x_3y: 2 }).flatten()) self.assertEqual( Tbn({ self.x: 6, self.y: 8 }), Configuration({ self.polymer_1x_1y: 2, self.polymer_2x_3y: 2 }).flatten()) self.assertEqual( Tbn({ self.x: 1, self.y: infinity }), Configuration({ self.polymer_1x: 1, self.polymer_1y: infinity }).flatten()) self.assertEqual( Tbn({ self.x: 1, self.y: infinity }), Configuration({ self.polymer_1x_1y: 1, self.polymer_1y: infinity }).flatten()) self.assertEqual( Tbn({ self.x: infinity, self.y: infinity }), Configuration({ self.polymer_1x_1y: infinity, self.polymer_1y: 1 }).flatten())
def test_stable_config(self): test_cases = [ ("a* b* \n a b \n a* \n b*", 3, 1, self.cp_solver, SolverFormulation.BOND_AWARE_NETWORK), ("a* b* \n a b \n a* \n b*", 3, 1, self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("a* b* \n a b \n a* \n b*", 3, 1, self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("a* b* \n a b \n a* \n b*", 3, 1, self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("a* b* \n a b \n a* \n b*", 3, 1, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("a* b* \n a b \n a* \n b*", 3, 1, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("a* b* \n a b \n a* \n b*", 3, 1, self.cp_solver, SolverFormulation.HILBERT_BASIS), ("a* b* \n a b \n a* \n b*", 3, 1, self.ip_solver, SolverFormulation.BOND_AWARE_NETWORK), ("a* b* \n a b \n a* \n b*", 3, 1, self.ip_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("a* b* \n a b \n a* \n b*", 3, 1, self.ip_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("a* b* \n a b \n a* \n b*", 3, 1, self.ip_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("a* b* \n a b \n a* \n b*", 3, 1, self.ip_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("a* b* \n a b \n a* \n b*", 3, 1, self.ip_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("a* b* \n a b \n a* \n b*", 3, 1, self.ip_solver, SolverFormulation.HILBERT_BASIS), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.BOND_AWARE_NETWORK), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.HILBERT_BASIS), ("2[a* b*] \n a b", 2, 1, self.ip_solver, SolverFormulation.BOND_AWARE_NETWORK), ("2[a* b*] \n a b", 2, 1, self.ip_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("2[a* b*] \n a b", 2, 1, self.ip_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("2[a* b*] \n a b", 2, 1, self.ip_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("2[a* b*] \n a b", 2, 1, self.ip_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("2[a* b*] \n a b", 2, 1, self.ip_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("2[a* b*] \n a b", 2, 1, self.ip_solver, SolverFormulation.HILBERT_BASIS), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.cp_solver, SolverFormulation.HILBERT_BASIS), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.ip_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.ip_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.ip_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.ip_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.ip_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 2, 5, self.ip_solver, SolverFormulation.HILBERT_BASIS), ("inf[a* b*] \n 2[a b]", infinity, 2, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("inf[a* b*] \n 2[a b]", infinity, 2, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("inf[a* b*] \n 2[a b]", infinity, 2, self.ip_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("inf[a* b*] \n 2[a b]", infinity, 2, self.ip_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", infinity, 4, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", infinity, 4, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", infinity, 4, self.ip_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", infinity, 4, self.ip_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ] for tbn_string, number_of_polymers, number_of_merges, solver, formulation in test_cases: with self.subTest(tbn_string=tbn_string, solver=solver, formulation=formulation): test_tbn = Tbn.from_string(tbn_string) configuration = solver.stable_config(test_tbn, formulation=formulation, bond_weighting_factor=2.0) self.assertEqual(number_of_polymers, configuration.number_of_polymers()) self.assertEqual(number_of_merges, configuration.number_of_merges()) low_w_test_cases = [ ("a* b* \n a b \n a* \n b*", 4, 0, self.cp_solver, 0.4), ("a* b* \n a b \n a* \n b*", 4, 0, self.ip_solver, 0.4), ("a* b* \n a b \n a* \n b*", 3, 1, self.cp_solver, 0.6), ("a* b* \n a b \n a* \n b*", 3, 1, self.ip_solver, 0.6), ("2[a* b*] \n a b", 3, 0, self.cp_solver, 0.4), ("2[a* b*] \n a b", 3, 0, self.ip_solver, 0.4), ("2[a* b*] \n a b", 2, 1, self.cp_solver, 0.6), ("2[a* b*] \n a b", 2, 1, self.ip_solver, 0.6), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 5, 2, self.cp_solver, 0.4), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 5, 2, self.ip_solver, 0.4), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 4, 3, self.cp_solver, 0.6), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 4, 3, self.ip_solver, 0.6), ("inf[a* b*] \n 2[a b]", infinity, 0, self.cp_solver, 0.4), ("inf[a* b*] \n 2[a b]", infinity, 0, self.ip_solver, 0.4), ("inf[a* b*] \n 2[a b]", infinity, 2, self.cp_solver, 0.6), ("inf[a* b*] \n 2[a b]", infinity, 2, self.ip_solver, 0.6), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", infinity, 2, self.cp_solver, 0.4), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", infinity, 2, self.ip_solver, 0.4), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", infinity, 4, self.cp_solver, 0.6), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", infinity, 4, self.ip_solver, 0.6), ] for tbn_string, number_of_polymers, number_of_merges, solver, weight in low_w_test_cases: with self.subTest("low w tests", tbn_string=tbn_string, solver=solver, weight=weight): test_tbn = Tbn.from_string(tbn_string) configuration = solver.stable_config( test_tbn, formulation=SolverFormulation.VARIABLE_BOND_WEIGHT, bond_weighting_factor=weight, ) self.assertEqual(number_of_polymers, configuration.number_of_polymers()) self.assertEqual(number_of_merges, configuration.number_of_merges())
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")
def test_stable_configs(self): test_cases = [ ("a* b* \n a b \n a* \n b*", 1, 1, self.cp_solver, SolverFormulation.BOND_AWARE_NETWORK), ("a* b* \n a b \n a* \n b*", 1, 1, self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("a* b* \n a b \n a* \n b*", 1, 1, self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("a* b* \n a b \n a* \n b*", 1, 1, self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("a* b* \n a b \n a* \n b*", 1, 1, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("a* b* \n a b \n a* \n b*", 1, 1, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("a* b* \n a b \n a* \n b*", 1, 1, self.cp_solver, SolverFormulation.HILBERT_BASIS), ("a a \n a* a*", 2, 1, self.cp_solver, SolverFormulation.BOND_AWARE_NETWORK), ("a a \n a* a*", 1, 1, self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("a a \n a* a*", 1, 1, self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("a a \n a* a*", 1, 1, self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("a a \n a* a*", 1, 1, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("a a \n a* a*", 1, 1, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("a a \n a* a*", 1, 1, self.cp_solver, SolverFormulation.HILBERT_BASIS), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.BOND_AWARE_NETWORK), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("2[a* b*] \n a b", 2, 1, self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("2[a* b*] \n a b", 1, 1, self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("2[a* b*] \n a b", 1, 1, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("2[a* b*] \n a b", 1, 1, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("2[a* b*] \n a b", 1, 1, self.cp_solver, SolverFormulation.HILBERT_BASIS), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 4, 5, self.cp_solver, SolverFormulation.BOND_OBLIVIOUS_NETWORK), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 4, 5, self.cp_solver, SolverFormulation.POLYMER_BINARY_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 3, 5, self.cp_solver, SolverFormulation.POLYMER_INTEGER_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 3, 5, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 3, 5, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 3, 5, self.cp_solver, SolverFormulation.HILBERT_BASIS), ("inf[a* b*] \n 2[a b]", 1, 2, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("inf[a* b*] \n 2[a b]", 1, 2, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", 2, 4, self.cp_solver, SolverFormulation.POLYMER_UNBOUNDED_MATRIX), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", 2, 4, self.cp_solver, SolverFormulation.VARIABLE_BOND_WEIGHT), ] for tbn_string, number_of_configs, number_of_merges, solver, formulation in test_cases: with self.subTest(tbn_string=tbn_string, solver=solver, formulation=formulation): test_tbn = Tbn.from_string(tbn_string) configurations = list( solver.stable_configs(test_tbn, formulation=formulation, bond_weighting_factor=2.0)) self.assertEqual(number_of_configs, len(configurations)) for configuration in configurations: self.assertEqual(number_of_merges, configuration.number_of_merges()) low_w_test_cases = [ ("a* b* \n a b \n a* \n b*", 1, 0.8, self.cp_solver, 0.4), ("a* b* \n a b \n a* \n b*", 1, 1.0, self.cp_solver, 0.6), ("2[a* b*] \n a b", 1, 0.8, self.cp_solver, 0.4), ("2[a* b*] \n a b", 1, 1.0, self.cp_solver, 0.6), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 1, 3.6, self.cp_solver, 0.4), ("6(a*) \n 2[3(a*)] \n a \n 5(a) \n 2(a) \n 4(a)", 1, 4.2, self.cp_solver, 0.6), ("inf[a* b*] \n 2[a b]", 1, 1.6, self.cp_solver, 0.4), ("inf[a* b*] \n 2[a b]", 1, 2.0, self.cp_solver, 0.6), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", 1, 3.6, self.cp_solver, 0.4), ("inf[2(a*) 2(b*)] \n 2[3(a) 3(b)]", 2, 4.0, self.cp_solver, 0.6), ] for tbn_string, number_of_configs, energy, solver, weight in low_w_test_cases: with self.subTest("low w tests", tbn_string=tbn_string, solver=solver, weight=weight): test_tbn = Tbn.from_string(tbn_string) configurations = list( solver.stable_configs( test_tbn, formulation=SolverFormulation.VARIABLE_BOND_WEIGHT, bond_weighting_factor=weight, )) self.assertEqual(number_of_configs, len(configurations)) for configuration in configurations: self.assertEqual(energy, configuration.energy(weight))
def get_tbn_from_filename(tbn_filename) -> Tbn: with open(tbn_filename) as tbnFile: tbn_as_string = tbnFile.read() return Tbn.from_string(tbn_as_string)