Skip to content

Instantly share code, notes, and snippets.

@battis
Last active December 17, 2015 13:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save battis/5614823 to your computer and use it in GitHub Desktop.
Save battis/5614823 to your computer and use it in GitHub Desktop.
{
"metadata": {
"name": "Balanced Equation"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": "Julie, I have defined a couple of helper functions \u2014 matrixFromEquation() and computeCoefficients() \u2014 and pulled them together, along with your gaussianElmination() function into a master balanceEquation() function which does just that.\n\nIf you read through this code, you'll see that gaussianElmination() is still\u2026 unfinished. (I left that for you!) But if you run this code \"as is\" right now, it will output our \"standard\" (list of lists of dictionaries of dictionaries) form of the balanced equation."
},
{
"cell_type": "code",
"collapsed": false,
"input": "def matrixFromEquation(equation):\n '''\n Create a matrix that represents N linear equations for each of the N\n molecular coefficients of the unbalanced equation, of the form\n\n a + b - x = y\n\n with one linear equation representing the relationship among the\n molecules for each element present in the equation\n '''\n # make a copy of equation so we don't accidentally change it (lists are mutable!)\n myEquation = equation[:]\n\n # algebraically subtract all but the last product from the reactants\n algebraicEquation = myEquation[0] + myEquation[1]\n subtractedMolecules = list(range(len(myEquation[0]), len(algebraicEquation) - 1))\n for i in subtractedMolecules:\n algebraicEquation[i]['coefficient'] *= -1\n\n # build our matrix as a dictionary\n matrix = dict()\n # ...creating a row for each element's linear equation as we first encounter it\n for molecule in algebraicEquation:\n for symbol, subscript in molecule['formula'].items():\n if not symbol in matrix: # meaning we haven't yet processed it\n matrix[symbol] = list()\n for m in algebraicEquation:\n if symbol in m['formula']:\n matrix[symbol].append(m['coefficient'] * m['formula'][symbol])\n else:\n matrix[symbol].append(0)\n\n # convert the dictionary into a list\n finalMatrix = list()\n for symbol, row in matrix.items():\n finalMatrix.append(row)\n\n return finalMatrix\n\ndef gaussianElimination(matrix):\n '''\n Some assumptions that you can make about this matrix:\n - It will always be a list of lists.\n - It might have more or less rows and more or less columns than this sample.\n - It can be called anything you like (name your parameter whatever you'd like).\n - I can provide a handy function to convert the solved matrix back into a balanced equation.\n - You may want to write a few \"helper\" functions to support your function.\n - You will want your solved matrix to end up in \"diagonal\" form:\n\n [[1, 0, 0, 0.333],\n [0, 1, 0, 1.666],\n [0, 0, 1, 1.333]]\n\n '''\n\n # JULIE'S CODE GOES HERE\n\n # obviously, this only works for one specific case...\n return [[1, 0, 0, 0.333],\n [0, 1, 0, 1.666],\n [0, 0, 1, 1.333]]\n\ndef computeCoefficients(matrix, solvedMatrix):\n '''\n Working from the original matrix and its diagonal form (provided,\n no doubt, by gaussianElimination()), compute the whole number, simplest\n coefficients, based on the linear equations represented in the\n matrices.\n '''\n # collect rational solution to matrix\n coefficients = list()\n for row in solvedMatrix:\n coefficients.append(row[-1])\n coefficients.append(1) # don't forget our last coefficient!\n\n # calculate multiplier to convert rational numbers to whole numbers\n multiplier = 1\n for row in matrix:\n for column in row:\n if column != 0:\n multiplier = multiplier * abs(column)\n\n # apply multiplier to solutions\n coefficientCount = list(range(len(coefficients)))\n for i in coefficientCount:\n # we _should_ get a whole number, but we may have lost precision, so we round\n coefficients[i] = round(coefficients[i] * multiplier)\n\n # calculate the greatest common divisor of all the coefficients\n import fractions\n gcd = coefficients[0]\n for i in coefficientCount:\n gcd = fractions.gcd(gcd, coefficients[i])\n\n # divide all coefficients by the greatest common divisor\n for i in coefficientCount:\n # these are whole numbers, lets force them to be ints, rather than floats!\n coefficients[i] = int(coefficients[i] / gcd)\n\n return coefficients\n\ndef balanceEquation(equation):\n '''\n Given an unbalanced chemical equation in our \"standard\" form -- a\n list of lists of dictionaries of dictionaries -- calculate the\n molecular coefficients necessary to balance that equation and\n return a copy of the balanced equation.\n '''\n\n # generate a matrix of linear equations for the coefficients\n matrix = matrixFromEquation(equation);\n\n # use Gaussian elimination to solve for each coefficient\n solvedMatrix = gaussianElimination(matrix)\n\n # compute the simplest, whole number coefficients\n coefficients = computeCoefficients(matrix, solvedMatrix)\n\n # make a copy of the equation (lists are mutable!)\n balancedEquation = equation[:]\n\n # assign coefficients to the balancedEquation\n coefficientCount = list(range(len(coefficients)))\n for i in coefficientCount:\n if i < len(balancedEquation[0]):\n balancedEquation[0][i]['coefficient'] = coefficients[i]\n else:\n balancedEquation[1][i - len(balancedEquation[0])]['coefficient'] = coefficients[i]\n\n return balancedEquation\n\n'''\n Some handy equations that someone else left lying around a few days ago...\n'''\n\ndef printEquation(equation):\n for side in equation:\n for molecule in side:\n printOperand(molecule)\n if molecule != side[-1]:\n print(' + ', sep = '', end = '')\n if side != equation[-1]:\n print(' --> ', sep = '', end = '')\n print()\n\ndef printOperand(operand):\n if operand['coefficient'] > 1:\n print(operand['coefficient'], '(', sep = '', end = '')\n printMolecule(operand['formula'])\n if operand['coefficient'] > 1:\n print(')', sep = '', end = '')\n\ndef printMolecule(molecule):\n # FIXME why does this print in the wrong order?\n for symbol, subscript in molecule.items():\n if subscript > 1:\n print(symbol, subscript, sep = '_', end = '')\n else:\n print(symbol, sep = '', end = '')\n\nequation = [\n [\n {'coefficient': 1,\n 'formula': {\n 'C': 3,\n 'H': 8\n }},\n {'coefficient': 1,\n 'formula': {\n 'O': 2\n }}\n ],\n [\n {'coefficient': 1,\n 'formula': {\n 'H': 2,\n 'O': 1\n }},\n {'coefficient': 1,\n 'formula': {\n 'C': 1,\n 'O': 2\n }}\n ]\n]\n\nprint('Unbalanced:')\nprintEquation(equation)\nprint('Balanced:')\nprintEquation(balanceEquation(equation))",
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment