def clumsy_solve(A: sp.Matrix, b: sp.Matrix): """Fixes a bug in diophantine.solve by expanding the system of equations to force multiple solutions.""" try: # First try to find 0 or infinite solutions. x = solve(A, b) return x except NotImplementedError: # Expand the system to get more than 1 solutions (infinite, # since nontrivial kernel). Then drop the last element of # the solution to get the solution of the original unexpanded # system. A_inf = sp.Matrix.hstack(A, sp.Matrix.zeros(A.shape[0], 1)) # Expand system. x = solve(A_inf, b) # infinite solutions so no error ... return [sp.Matrix(x[0][:-1])] # Drop the last element of the vector.
def dimension_to_units_compound(cls, dimension): """ Projects a given unit onto a list of units that span the space of dimensions present in the unit to project. Returns a list of the basis units with their associated powers and the scale of the presented units. """ if dimension == 1 or dimension == un.dimensionless: return 0, [] if isinstance(dimension, sympy.Basic): dimension = un.Dimension.from_sympy(dimension) else: assert isinstance(dimension, un.Dimension), ( "'{}' is not a Dimension".format(dimension)) # Check to see if unit dimension, or some integer power thereof, # has been stored in the cache (the basis and compounds are preloaded) dim_vector = array(list(dimension), dtype='float') # mask of units in compound mask = (dim_vector != 0) # Get the coefficients required to transform the basis dim elements # into the provided dimension, and test to see if they are constant with_scalars = [(x, numpy.unique(dim_vector[mask] / numpy.asarray(d)[mask])) for d, x in cls.cache.items() if ((numpy.asarray(d) != 0) == mask).all()] matches = [(u, int(s[0])) for u, s in with_scalars if len(s) == 1 and float(s[0]).is_integer()] assert len(matches) <= 1, ( "There should not be matches for multiple basis/compound units, " "the dimension vector of one must be a factor of an another") # If there is a match and the scalar is an integer then use that unit # basis/compound. if matches and float(matches[0][1]).is_integer(): base_x, scalar = matches[0] scalar = int(scalar) num_compounds = len(nonzero(base_x[len(cls.basis):])[0]) assert num_compounds <= 1, ( "Multiple compound indices matched (x={})".format(base_x)) assert xor(base_x[:len(cls.basis)].any(), num_compounds != 0), ( "Mix of basis vectors and compounds (x={})".format(base_x)) x = base_x * scalar # If there is not a direct relationship to a basis vector or special # compound, project the dimension onto the basis vectors, finding # the "minimal" solution (see _select_best_compound) else: # Get projection of dimension onto basis units b = array(list(dimension)) xs = diophantine.solve(cls.A, b) min_x = cls._select_best_compound(xs) x = numpy.concatenate((min_x, numpy.zeros(len(cls.compounds), dtype='int'))) cls.cache[tuple(dimension)] = x / int(abs(reduce(gcd, x))) # Get list of compound units with the powers compound = [(u, p) for u, p in zip(cls.specified_units, x) if p] # Calculate the appropriate scale for the new compound quantity exponent = int(x.dot([b.power for b in cls.specified_units])) return exponent, compound
def test_dimension_basis(self): """ This test comes from the mapping of compound dimensions (b) onto a new set of basis dimensions (A), where each row corresponds to the 7 basic dimensions ('mass', 'length', 'time', 'current', 'amount', 'temperature' and 'luminous intensity') The test cases correspond to: b_names = ['mV_per_ms', 'uF_per_cm2', 'uF_uS', 'K3_ms_uS4_per_cd2_cm', 'A2_mV_per_ms_uS', 'ms_uF3_um_per_mV', 'um3_per_C2', 'K2_per_mV_ms_ohm_uF'] A_names = [ ['ms', 'mV', 'mA_per_cm2', 'nA', 'mM', 'uF_per_cm2', 'um', 'S_per_cm2', 'uS', 'cm_ohm', 'ohm', 'degC', 'cd'], ['ms', 'mV', 'pA', 'mM', 'uF', 'um', 'uS', 'degC', 'cd']] """ As = [ Matrix([[0, 1, 0, 0, 0, -1, 0, -1, -1, 1, 1, 0, 0], [0, 2, -2, 0, -3, -4, 1, -4, -2, 3, 2, 0, 0], [1, -3, 0, 0, 0, 4, 0, 3, 3, -3, -3, 0, 0], [0, -1, 1, 1, 0, 2, 0, 2, 2, -2, -2, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]), Matrix([[0, 1, 0, 0, -1, 0, -1, 0, 0], [0, 2, 0, -3, -2, 1, -2, 0, 0], [1, -3, 0, 0, 4, 0, 3, 0, 0], [0, -1, 1, 0, 2, 0, 2, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1]]) ] bs = [ Matrix([1, 2, -4, -1, 0, 0, 0]), Matrix([-1, -4, 4, 2, 0, 0, 0]), Matrix([-2, -4, 7, 4, 0, 0, 0]), Matrix([-4, -9, 13, 8, 0, 3, -2]), Matrix([2, 4, -7, -1, 0, 0, 0]), Matrix([-4, -7, 16, 7, 0, 0, 0]), Matrix([0, 3, -2, -2, 0, 0, 0]), Matrix([-1, -2, 1, 1, 0, 2, 0]) ] num_sols = [(2, 2), (1, 1), (1, 1), (3, 1), (1, 2), (1, 1), (3, 1), (1, 1)] for b, nsols in zip(bs, num_sols): for A, nsol in zip(As, nsols): sols = solve(A, b) # Check number of solutions matches reference self.assertEquals( len(sols), nsol, "Incorrect number of solutions found ({}), " "expected {}".format(len(sols), nsol)) for sol in sols: self.assertEqual(b, A * sol, "A * x doesn't match b")
def solve_pt2(bus_id_offsets: List[BusIDOffset]) -> int: """Solving a system of linear Diophantine equations...""" first_, *bus_id_offsets = bus_id_offsets b = Matrix([(-1) * b.offset for b in bus_id_offsets]) # My input specific, do not have the time to make it generic... A = Matrix( [ [first_.bus_id, -bus_id_offsets[0].bus_id, 0, 0, 0, 0, 0, 0, 0], [first_.bus_id, 0, -bus_id_offsets[1].bus_id, 0, 0, 0, 0, 0, 0], [first_.bus_id, 0, 0, -bus_id_offsets[2].bus_id, 0, 0, 0, 0, 0], [first_.bus_id, 0, 0, 0, -bus_id_offsets[3].bus_id, 0, 0, 0, 0], [first_.bus_id, 0, 0, 0, 0, -bus_id_offsets[4].bus_id, 0, 0, 0], [first_.bus_id, 0, 0, 0, 0, 0, -bus_id_offsets[5].bus_id, 0, 0], [first_.bus_id, 0, 0, 0, 0, 0, 0, -bus_id_offsets[6].bus_id, 0], [first_.bus_id, 0, 0, 0, 0, 0, 0, 0, -bus_id_offsets[7].bus_id], ] ) sol_set = diophantine.solve(A, b)[0] coeff_for_17 = sol_set[0] adjust = prod([b.bus_id for b in bus_id_offsets]) if coeff_for_17 < 0: coeff_for_17 += adjust return coeff_for_17 * 17
import sys if __name__ == '__main__': schedule = [] with open(sys.argv[1]) as f: l = f.read().split('\n') for i, t in enumerate(l[1].split(',')): if not t == 'x': schedule.append((int(t), i)) combinations = itertools.combinations(schedule, 2) A = [] b = [] for x, y in combinations: row = [0] * len(schedule) vx, ix = x vy, iy = y row[schedule.index(x)] = vx row[schedule.index(y)] = -vy A.append(row) b.append(ix - iy) A = Matrix(A) b = Matrix(b) x = solve(A, b) print(x[0][0] * schedule[0][0])
def first_after_time(bus, time): cycles_before = time // bus wait_time = (cycles_before + 1) * bus - time return wait_time bus_wait = min([(first_after_time(b, start_time), b) for b in busses_int]) print('Part 1: ', bus_wait[0] * bus_wait[1]) busses = [(int(b), i) for i, b in enumerate(busses.split(',')) if b != 'x'] # busses = [(17, 0), (13, 2), (19, 3)] # busses = [(67, 0), (7, 1), (59, 2), (61, 3)] # A = Matrix([[1, 17, 0, 0], # [1, 0, 13, 0], # [1, 0, 0, 19]]) # b = Matrix([0, 2, 3]) # sol = solve(A, b)[0][0] A = zeros(len(busses), len(busses) + 1) for i in range(A.rows): A[i, 0] = -1 A[i, i + 1] = busses[i][0] b = Matrix([b[1] for b in busses]) sol = solve(A, b) print('Part 2: ', sol[0][0])
def test_random(self): As = [ Matrix([[0, 3, -7, 7, -5, 4, 4, -1, -5, -9], [-9, -2, -1, 9, -6, 9, 1, 8, -1, 8], [4, 4, -4, 2, 4, 2, 5, 3, 9, 0], [4, 3, -5, 9, -2, 1, -7, 2, 2, 8], [7, 6, 5, -2, -9, -2, 0, 6, -2, -3]]), Matrix([[-5, 8, 4, -6, 7, 1, 0, 5, 8, -3], [1, -8, 0, 7, -4, 2, 4, -8, 5, 1], [-2, 0, -1, 5, -3, -2, 8, -4, -3, 8], [0, 5, -6, 1, 2, -3, -2, -5, -1, -9], [-2, 3, 1, -1, 7, -5, -9, -5, 4, -4]]), Matrix([[-5, 2, 7, -8, 3, -5, -8, -8, -1, -5], [-3, 5, 3, 5, 3, -8, -6, -6, -1, 5], [-5, -5, -5, 9, 9, 0, -2, 2, 5, -7], [0, 0, 1, -1, 1, -4, 9, 7, 8, 4], [8, -3, -1, 3, 1, 8, 6, 0, -2, -5]]), Matrix([[-5, 1, -1, 5, -3, 0, -7, 4, -9, 5], [-3, -6, 8, 3, 1, -7, 5, -3, 2, 3], [-7, 8, 3, -3, -7, 9, -5, -8, -8, 2], [6, -2, -3, -8, 1, -8, -4, -4, -7, 8], [0, -6, 1, 8, -6, -1, -1, -4, 4, 4]]), Matrix([[-5, -2, 2, -7, 3, 0, -6, -8, -1, -5], [-3, 0, 3, 4, 3, 4, 2, 3, -3, -9], [9, 0, -1, 5, 8, -2, 4, 8, -9, 1], [4, 3, 1, 2, 8, -6, 0, 0, 9, 3], [2, 2, -8, 0, 6, -2, -8, -6, 4, -9]]), Matrix([[4, -9, 4, -7, 8, -1, 1, -9, -8, -6], [-7, -2, 7, -4, -7, 9, 5, 4, -8, 3], [-2, -2, 5, 8, -5, 8, 5, -1, 3, 5], [-4, -4, 7, 2, -2, 2, 1, 7, -9, 2], [-8, -9, -4, -4, 1, 0, 2, -5, -5, 6]]), Matrix([[-5, 8, -2, -5, -1, -8, -5, -1, 5, 2], [-4, 3, -5, -2, -9, -8, 2, -8, 8, -1], [-2, 3, 0, -6, 2, 3, -1, 2, 9, -6], [9, -4, 1, -7, -1, 3, 2, 4, 6, 6], [-9, 6, -1, -8, 2, 1, 5, 5, -8, 8]]), Matrix([[9, 4, -7, -5, -4, 5, -7, 8, -5, -3], [7, -9, -2, -9, 8, 1, -6, -9, -3, -2], [-4, 4, 2, 7, -1, -5, 0, -5, -7, -9], [-8, 5, 6, -9, 8, 4, 7, -4, -1, 5], [3, -8, -6, 2, 8, -3, 9, -9, -9, -4]]), Matrix([[-9, 9, 9, 8, 2, 2, -8, 8, 4, -8], [-3, -7, -6, 6, -4, 7, 5, -6, 1, 1], [-9, 4, -2, 9, 9, 6, -5, 7, 8, 2], [7, -9, -5, 6, -2, 6, 6, 4, 2, 7], [7, 4, 9, 8, -4, -4, -9, 1, -9, 0]]), Matrix([[-2, 2, 4, 9, 3, 9, -5, -7, -3, 5], [-1, -2, 7, -2, 9, -2, 3, 9, -9, 0], [0, 0, 6, 0, -3, -9, 7, -2, 8, -4], [9, -2, -3, -3, 6, 6, -8, -8, 9, -6], [-7, -2, -5, 1, 9, 7, 3, 5, -8, -9]]) ] bs = [ Matrix([-2, 0, -7, 1, 2]), Matrix([2, 6, -3, -6, 8]), Matrix([-8, 4, -8, -5, -2]), Matrix([-1, -9, 0, -4, 5]), Matrix([7, -6, -4, 1, 0]), Matrix([-7, 4, 7, -8, 9]), Matrix([-1, 0, 2, -8, 6]), Matrix([-9, 6, 7, -7, -7]), Matrix([-6, -3, -4, -8, 5]), Matrix([-2, -2, 6, 2, 4]) ] nsols = [ 1, 1, 2, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] count = 0 for A in As: for b in bs: xs = solve(A, b) self.assertEqual(len(xs), nsols[count], "Incorrect number of solutions") for x in xs: self.assertEquals(A * x, b, "A * x doesn't match b") count += 1
def test_random(self): As = [ Matrix([[0, 3, -7, 7, -5, 4, 4, -1, -5, -9], [-9, -2, -1, 9, -6, 9, 1, 8, -1, 8], [4, 4, -4, 2, 4, 2, 5, 3, 9, 0], [4, 3, -5, 9, -2, 1, -7, 2, 2, 8], [7, 6, 5, -2, -9, -2, 0, 6, -2, -3]]), Matrix([[-5, 8, 4, -6, 7, 1, 0, 5, 8, -3], [1, -8, 0, 7, -4, 2, 4, -8, 5, 1], [-2, 0, -1, 5, -3, -2, 8, -4, -3, 8], [0, 5, -6, 1, 2, -3, -2, -5, -1, -9], [-2, 3, 1, -1, 7, -5, -9, -5, 4, -4]]), Matrix([[-5, 2, 7, -8, 3, -5, -8, -8, -1, -5], [-3, 5, 3, 5, 3, -8, -6, -6, -1, 5], [-5, -5, -5, 9, 9, 0, -2, 2, 5, -7], [0, 0, 1, -1, 1, -4, 9, 7, 8, 4], [8, -3, -1, 3, 1, 8, 6, 0, -2, -5]]), Matrix([[-5, 1, -1, 5, -3, 0, -7, 4, -9, 5], [-3, -6, 8, 3, 1, -7, 5, -3, 2, 3], [-7, 8, 3, -3, -7, 9, -5, -8, -8, 2], [6, -2, -3, -8, 1, -8, -4, -4, -7, 8], [0, -6, 1, 8, -6, -1, -1, -4, 4, 4]]), Matrix([[-5, -2, 2, -7, 3, 0, -6, -8, -1, -5], [-3, 0, 3, 4, 3, 4, 2, 3, -3, -9], [9, 0, -1, 5, 8, -2, 4, 8, -9, 1], [4, 3, 1, 2, 8, -6, 0, 0, 9, 3], [2, 2, -8, 0, 6, -2, -8, -6, 4, -9]]), Matrix([[4, -9, 4, -7, 8, -1, 1, -9, -8, -6], [-7, -2, 7, -4, -7, 9, 5, 4, -8, 3], [-2, -2, 5, 8, -5, 8, 5, -1, 3, 5], [-4, -4, 7, 2, -2, 2, 1, 7, -9, 2], [-8, -9, -4, -4, 1, 0, 2, -5, -5, 6]]), Matrix([[-5, 8, -2, -5, -1, -8, -5, -1, 5, 2], [-4, 3, -5, -2, -9, -8, 2, -8, 8, -1], [-2, 3, 0, -6, 2, 3, -1, 2, 9, -6], [9, -4, 1, -7, -1, 3, 2, 4, 6, 6], [-9, 6, -1, -8, 2, 1, 5, 5, -8, 8]]), Matrix([[9, 4, -7, -5, -4, 5, -7, 8, -5, -3], [7, -9, -2, -9, 8, 1, -6, -9, -3, -2], [-4, 4, 2, 7, -1, -5, 0, -5, -7, -9], [-8, 5, 6, -9, 8, 4, 7, -4, -1, 5], [3, -8, -6, 2, 8, -3, 9, -9, -9, -4]]), Matrix([[-9, 9, 9, 8, 2, 2, -8, 8, 4, -8], [-3, -7, -6, 6, -4, 7, 5, -6, 1, 1], [-9, 4, -2, 9, 9, 6, -5, 7, 8, 2], [7, -9, -5, 6, -2, 6, 6, 4, 2, 7], [7, 4, 9, 8, -4, -4, -9, 1, -9, 0]]), Matrix([[-2, 2, 4, 9, 3, 9, -5, -7, -3, 5], [-1, -2, 7, -2, 9, -2, 3, 9, -9, 0], [0, 0, 6, 0, -3, -9, 7, -2, 8, -4], [9, -2, -3, -3, 6, 6, -8, -8, 9, -6], [-7, -2, -5, 1, 9, 7, 3, 5, -8, -9]]) ] bs = [ Matrix([-2, 0, -7, 1, 2]), Matrix([2, 6, -3, -6, 8]), Matrix([-8, 4, -8, -5, -2]), Matrix([-1, -9, 0, -4, 5]), Matrix([7, -6, -4, 1, 0]), Matrix([-7, 4, 7, -8, 9]), Matrix([-1, 0, 2, -8, 6]), Matrix([-9, 6, 7, -7, -7]), Matrix([-6, -3, -4, -8, 5]), Matrix([-2, -2, 6, 2, 4]) ] nsols = [ 1, 1, 2, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] count = 0 for A in As: for b in bs: xs = solve(A, b) self.assertEqual( len(xs), nsols[count], "Incorrect number of solutions") for x in xs: self.assertEquals(A * x, b, "A * x doesn't match b") count += 1
import itertools from sympy import Matrix from diophantine import solve if __name__ == '__main__': with open('input', 'r') as f: time = int(f.readline()) pos_bus_ids = [(idx, int(bus_id)) for idx, bus_id in enumerate(f.readline().split(',')) if bus_id != 'x'] combs = itertools.combinations(pos_bus_ids, 2) matrix = [] sols = [] for comb1, comb2 in combs: row = [0 for _ in pos_bus_ids] row[pos_bus_ids.index(comb1)] = comb1[1] row[pos_bus_ids.index(comb2)] = -comb2[1] matrix.append(row) sols.append(comb1[0] - comb2[0]) M = Matrix(matrix) s = Matrix(sols) r = solve(M, s) print(r[0][0] * pos_bus_ids[0][1])