Repository: sympy/sympy

Issue:
codegen octave global vars should print "global foo" at top of function
After #9535, we should improve the octave printing further to print `global foo bar` in the generated function.

Discussion:

Patch:
diff --git a/sympy/utilities/codegen.py b/sympy/utilities/codegen.py
index ef653b5b99..bb7eefc2ec 100644
--- a/sympy/utilities/codegen.py
+++ b/sympy/utilities/codegen.py
@@ -669,6 +669,7 @@ def dump_code(self, routines, f, prefix, header=True, empty=True):
                 code_lines.append("\n")
             code_lines.extend(self._get_routine_opening(routine))
             code_lines.extend(self._declare_arguments(routine))
+            code_lines.extend(self._declare_globals(routine))
             code_lines.extend(self._declare_locals(routine))
             if empty:
                 code_lines.append("\n")
@@ -766,6 +767,10 @@ def _declare_arguments(self, routine):
         # arguments are declared in prototype
         return []
 
+    def _declare_globals(self, routine):
+        # global variables are not explicitly declared within C functions
+        return []
+
     def _declare_locals(self, routine):
         # loop variables are declared in loop statement
         return []
@@ -969,6 +974,11 @@ def _declare_arguments(self, routine):
 
         return code_list
 
+    def _declare_globals(self, routine):
+        # Global variables not explicitly declared within Fortran 90 functions.
+        # Note: a future F77 mode may need to generate "common" blocks.
+        return []
+
     def _declare_locals(self, routine):
         code_list = []
         for var in sorted(routine.local_vars, key=str):
@@ -1246,6 +1256,12 @@ def _get_routine_opening(self, routine):
     def _declare_arguments(self, routine):
         return []
 
+    def _declare_globals(self, routine):
+        if not routine.global_vars:
+            return []
+        s = " ".join(sorted([self._get_symbol(g) for g in routine.global_vars]))
+        return ["global " + s + "\n"]
+
     def _declare_locals(self, routine):
         return []
 
@@ -1302,6 +1318,7 @@ def dump_m(self, routines, f, prefix, header=True, empty=True, inline=True):
                                       "  Autogenerated by sympy\n")
                     code_lines.append(''.join(self._get_header()))
             code_lines.extend(self._declare_arguments(routine))
+            code_lines.extend(self._declare_globals(routine))
             code_lines.extend(self._declare_locals(routine))
             if empty:
                 code_lines.append("\n")


Test patch:
diff --git a/sympy/utilities/tests/test_codegen_octave.py b/sympy/utilities/tests/test_codegen_octave.py
index 5fe36f2dda..3d6bce7d94 100644
--- a/sympy/utilities/tests/test_codegen_octave.py
+++ b/sympy/utilities/tests/test_codegen_octave.py
@@ -566,6 +566,7 @@ def test_m_not_supported():
     )
     assert source == expected
 
+
 def test_global_vars_octave():
     x, y, z, t = symbols("x y z t")
     result = codegen(('f', x*y), "Octave", header=False, empty=False,
@@ -573,6 +574,7 @@ def test_global_vars_octave():
     source = result[0][1]
     expected = (
         "function out1 = f(x)\n"
+        "  global y\n"
         "  out1 = x.*y;\n"
         "end\n"
         )
@@ -583,6 +585,7 @@ def test_global_vars_octave():
     source = result[0][1]
     expected = (
         "function out1 = f(x, y)\n"
+        "  global t z\n"
         "  out1 = x.*y + z;\n"
         "end\n"
     )