def solve_sym(a, b, epsilon=1e-6): """Given the symmetric matrix a and the vector b this returns a GBP object such that, after solve_trws() (prefered over the default solve_bp) has been called, the result() method returns x as the mean, such that a x = b, i.e. it solves the symmetric linear equation. This is poor compared to typical solvers as it suffers from the spectral radius being less than 1 requirement (typical of iterative methods) - really exists because I can, and its a good test that the system works. Its still useful if there are a lot of zeroes in (a) however, as it is stable with enough sparseness and becomes computationally efficient, though you may want to rewrite what it does to properlly utilise a sparse matrix class if that is the case. This is an implimentation of the paper 'Gaussian Belief Propagation Solver for Systems of Linear Equations' by Shental, Siegel, Wolf, Bickson and Dolev. The use of Gaussian TRW-S instead of Gaussian BP seems to make it converge far more often than otherwise, and I suspect is weakening some of the limitations discussed in the paper - it often works when Jacobi iterations (which it is almost equivalent to for normal BP) do not. Also note that the system has the weirdness of negative precision values when used for this, i.e. imaginary standard deviations. Make of this what you will, particularly the fact these often come out alongside the correct answer!""" assert(len(a.shape)==2) assert(len(b.shape)==1) assert(a.shape[0]==a.shape[1]) assert(a.shape[0]==b.shape[0]) ret = GBP(b.shape[0]) r = range(b.shape[0]) ret.unary_raw(r, b, a[r,r]) for i in r: for j in xrange(i+1, b.shape[0]): if numpy.fabs(a[i, j])>epsilon: ret.pairwise(i, j, float(a[i, j])) return ret
def solve_sym(a, b, epsilon=1e-6): """Given the symmetric matrix a and the vector b this returns a GBP object such that, after solve_trws() (prefered over the default solve_bp) has been called, the result() method returns x as the mean, such that a x = b, i.e. it solves the symmetric linear equation. This is poor compared to typical solvers as it suffers from the spectral radius being less than 1 requirement (typical of iterative methods) - really exists because I can, and its a good test that the system works. Its still useful if there are a lot of zeroes in (a) however, as it is stable with enough sparseness and becomes computationally efficient, though you may want to rewrite what it does to properlly utilise a sparse matrix class if that is the case. This is an implimentation of the paper 'Gaussian Belief Propagation Solver for Systems of Linear Equations' by Shental, Siegel, Wolf, Bickson and Dolev. The use of Gaussian TRW-S instead of Gaussian BP seems to make it converge far more often than otherwise, and I suspect is weakening some of the limitations discussed in the paper - it often works when Jacobi iterations (which it is almost equivalent to for normal BP) do not. Also note that the system has the weirdness of negative precision values when used for this, i.e. imaginary standard deviations. Make of this what you will, particularly the fact these often come out alongside the correct answer!""" assert (len(a.shape) == 2) assert (len(b.shape) == 1) assert (a.shape[0] == a.shape[1]) assert (a.shape[0] == b.shape[0]) ret = GBP(b.shape[0]) r = range(b.shape[0]) ret.unary_raw(r, b, a[r, r]) for i in r: for j in xrange(i + 1, b.shape[0]): if numpy.fabs(a[i, j]) > epsilon: ret.pairwise(i, j, float(a[i, j])) return ret
# Helper for below... def present(solver): iters = solver.solve() print ' iters =', iters mean, prec = solver.result() for i in xrange(0, mean.shape[0], 5): print ' mean: ' + ' '.join(['%7.2f'%v for v in mean[i:i+5]]) print ' precison: ' + ' '.join(['%7.2f'%v for v in prec[i:i+5]]) print # Start with a suitably boring graph... solver = GBP(1) solver.unary(0, 0.0, 1e3) print 'One node:' present(solver) # Make it more interesting... base = solver.add(4) indices = [base + i for i in xrange(4)] solver.pairwise(indices, [i-1 for i in indices], -1.0, 1e3) print 'Five, as chain:' present(solver)
# http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Verify that setting unary variance to infinity does the right thing (its not supported for pairwise terms)... for alg in ['bp', 'trw-s']: print 'Solve with %s:' % alg solver = GBP(6) solver.unary(0, -4.0, numpy.inf) solver.unary_sd(2, 93.0, 0.0) solver.unary_raw(4, -32.0, numpy.inf) # In raw case you send in the mean if precision is infinite! solver.pairwise([0,2,4], [1,3,5], 5.0, 1.0) if alg=='bp': solver.solve_bp() else: solver.solve_trws() mean, sd = solver.result_sd(slice(solver.node_count)) for i in xrange(solver.node_count): print ' %i: mean = %f, sd = %f' % (i, mean[i], sd[i])
# Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Chain 1... print 'Stretching to hit endpoints:' solver = GBP(8) solver.unary(0, 0.0, 15.0) solver.unary(7, 10.0, 15.0) solver.pairwise(slice(None,-1), slice(1, None), 0.0, 1.0) iters = solver.solve() print 'iters =', iters mean, prec = solver.result() print 'Mean: ' + ' '.join(['%.2f'%v for v in mean]) print 'Precison: ' + ' '.join(['%.2f'%v for v in prec]) print
# Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Grid 1... print 'Single Pivot:' solver = GBP(25) #5x5 solver.unary(12, 5.0, 4.0) for row in xrange(5): solver.pairwise(slice(row*5,(row+1)*5-1), slice(row*5+1,(row+1)*5), 1.0, 1.0) for col in xrange(5): solver.pairwise(slice(col,col+20,5), slice(col+5,col+25,5), 1.0, 1.0) iters = solver.solve() print 'iters =', iters for row in xrange(5): mean, prec = solver.result(slice(row*5, (row+1)*5)) print ' '.join(['%.4f'%v for v in mean])
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Grid 1... print 'Single Pivot:' solver = GBP(25) #5x5 solver.unary(12, 5.0, 4.0) for row in xrange(5): solver.pairwise(slice(row * 5, (row + 1) * 5 - 1), slice(row * 5 + 1, (row + 1) * 5), 1.0, 1.0) for col in xrange(5): solver.pairwise(slice(col, col + 20, 5), slice(col + 5, col + 25, 5), 1.0, 1.0) iters = solver.solve() print 'iters =', iters for row in xrange(5): mean, prec = solver.result(slice(row * 5, (row + 1) * 5))
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Test the ability to replace values using the prev_exp keyword parameter - minimal test as its almost the same code path as everything else... solver = GBP(2) for ii in xrange(3): print 'V%i:' % ii if ii == 0: solver.unary_sd(0, 50.0, 0.1) solver.pairwise_sd(0, 1, 10.0, 2.0) else: pe = 1.0 if ii == 1 else 0.0 solver.unary_sd(0, -100.0, 0.5, prev_exp=pe) solver.pairwise_sd(0, 1, 20.0, 1.0, prev_exp=pe) solver.solve() mean, sd = solver.result_sd(slice(solver.node_count)) for i in xrange(solver.node_count):
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Test the ability to replace values using the prev_exp keyword parameter - minimal test as its almost the same code path as everything else... solver = GBP(2) for ii in xrange(3): print 'V%i:' % ii if ii==0: solver.unary_sd(0, 50.0, 0.1) solver.pairwise_sd(0, 1, 10.0, 2.0) else: pe = 1.0 if ii==1 else 0.0 solver.unary_sd(0, -100.0, 0.5, prev_exp = pe) solver.pairwise_sd(0, 1, 20.0, 1.0, prev_exp = pe) solver.solve() mean, sd = solver.result_sd(slice(solver.node_count))
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Verify the raw access methods that utilise p-mean/p-offset work... solver = GBP(2) solver.unary_raw(0, 30.0 / 0.01, 1.0 / 0.01) solver.pairwise_raw(0, 1, 60.0 / 4.0, 1.0 / 4.0) solver.solve() pmean, prec = solver.result_raw(slice(solver.node_count)) for i in xrange(solver.node_count): print '%i: mean = %f, sd = %f' % (i, pmean[i] / prec[i], 1.0 / numpy.sqrt(prec[i]))
args = parser.parse_args() if args.output == '': args.output = os.path.splitext(args.input)[0] + '_uncurl.png' # Open the image... image = scipy.misc.imread(args.input) # Make floaty and normalise to handle quantisation error from the image encoding... image = image.astype(numpy.float32) - 127.0 div = numpy.sqrt(numpy.square(image).sum(axis=2)) image /= div[:, :, numpy.newaxis] # Setup a GBP object with a weak desire for each pixel to be on the plane... solver = GBP(image.shape[0] * image.shape[1]) solver.unary(slice(None), 0.0, args.plane) # Convert normals to gradients... image[:, :, :2] /= image[:, :, 2, numpy.newaxis] grad_x = -image[:, :, 0] grad_y = image[:, :, 1] # Add pairwise terms to GBP solver, using the averages of adjacent gradients... for y in xrange(image.shape[0]): g = 0.5 * (grad_x[y, :-1] + grad_x[y, 1:]) w = args.normal base = y * image.shape[1] solver.pairwise(slice(base, base + image.shape[1] - 1), slice(base + 1, base + image.shape[1]), g, w)
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Create a graph, solve it, print out the result... solver = GBP(9, 2) # Force it to do lots of blocking, so we can test that as well. solver.unary(4, 2.0, 10.0) solver.pairwise([4, 4, 3, 3, 5, 5], [3, 5, 0, 6, 2, 8], 1.0, 5.0) solver.solve() print 'H:' mean, prec = solver.result() print '% .3f % .3f % .3f' % (mean[0], mean[1], mean[2]) print '% .3f % .3f % .3f' % (mean[3], mean[4], mean[5]) print '% .3f % .3f % .3f' % (mean[6], mean[7], mean[8]) # Now reset everything... solver.reset_unary()
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Verify that setting unary variance to infinity does the right thing (its not supported for pairwise terms)... for alg in ['bp', 'trw-s']: print 'Solve with %s:' % alg solver = GBP(6) solver.unary(0, -4.0, numpy.inf) solver.unary_sd(2, 93.0, 0.0) solver.unary_raw( 4, -32.0, numpy.inf ) # In raw case you send in the mean if precision is infinite! solver.pairwise([0, 2, 4], [1, 3, 5], 5.0, 1.0) if alg == 'bp': solver.solve_bp() else: solver.solve_trws() mean, sd = solver.result_sd(slice(solver.node_count))
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Test the sd methods work, meh - I never use these myself anyway... solver = GBP(3) solver.unary_sd(0, 50.0, 0.1) solver.pairwise_sd(0, 1, 10.0, 2.0) solver.solve() mean, sd = solver.result_sd(slice(solver.node_count)) for i in xrange(solver.node_count): print '%i: mean = %f, sd = %f' % (i, mean[i], sd[i])
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Create a graph, solve it, print out the result... solver = GBP( 9, 2) # Force it to do lots of blocking, so we can test that as well. solver.unary(4, 2.0, 10.0) solver.pairwise([4, 4, 3, 3, 5, 5], [3, 5, 0, 6, 2, 8], 1.0, 5.0) solver.solve() print 'H:' mean, prec = solver.result() print '% .3f % .3f % .3f' % (mean[0], mean[1], mean[2]) print '% .3f % .3f % .3f' % (mean[3], mean[4], mean[5]) print '% .3f % .3f % .3f' % (mean[6], mean[7], mean[8]) # Now reset everything... solver.reset_unary() solver.reset_pairwise() # Add new links and data, solve again...
# Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Setup a chain... print 'Whole chain...' solver = GBP(5) solver.unary(0, 0.0, 15.0) solver.unary(4, 10.0, 15.0) solver.pairwise(slice(None,-1), slice(1, None), 0.5, 1.0) iters = solver.solve_bp() print 'iters =', iters mean, prec = solver.result() print 'Mean: ' + ' '.join(['%.2f'%v for v in mean]) print 'Precison: ' + ' '.join(['%.2f'%v for v in prec]) print
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Verify the raw access methods that utilise p-mean/p-offset work... solver = GBP(2) solver.unary_raw(0, 30.0 / 0.01, 1.0 / 0.01) solver.pairwise_raw(0, 1, 60.0 / 4.0 , 1.0 / 4.0) solver.solve() pmean, prec = solver.result_raw(slice(solver.node_count)) for i in xrange(solver.node_count): print '%i: mean = %f, sd = %f' % (i, pmean[i]/prec[i], 1.0 / numpy.sqrt(prec[i]))
#! /usr/bin/env python # Copyright 2014 Tom SF Haines # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. import numpy from gbp import GBP # Setup a chain... print 'Whole chain...' solver = GBP(5) solver.unary(0, 0.0, 15.0) solver.unary(4, 10.0, 15.0) solver.pairwise(slice(None, -1), slice(1, None), 0.5, 1.0) iters = solver.solve_bp() print 'iters =', iters mean, prec = solver.result() print 'Mean: ' + ' '.join(['%.2f' % v for v in mean]) print 'Precison: ' + ' '.join(['%.2f' % v for v in prec]) print # Disable middle and resolve... print 'Middle gone...'
# Open the image... image = scipy.misc.imread(args.input) # Make floaty and normalise to handle quantisation error from the image encoding... image = image.astype(numpy.float32) - 127.0 div = numpy.sqrt(numpy.square(image).sum(axis=2)) image /= div[:,:,numpy.newaxis] # Setup a GBP object with a weak desire for each pixel to be on the plane... solver = GBP(image.shape[0] * image.shape[1]) solver.unary(slice(None), 0.0, args.plane) # Convert normals to gradients... image[:,:,:2] /= image[:,:,2,numpy.newaxis] grad_x = -image[:,:,0] grad_y = image[:,:,1] # Add pairwise terms to GBP solver, using the averages of adjacent gradients... for y in xrange(image.shape[0]): g = 0.5 * (grad_x[y,:-1] + grad_x[y,1:]) w = args.normal