# A macro for pretty geometric algebra equations?

**URL:** <https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664>\
**Category:** General Usage\
**Tags:** macros, geometric-algebra\
**Created:** [December 15, 2022, 12:51am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664 "2022-12-15T00:51:52Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 15, 2022, 12:51am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/1 "2022-12-15T00:51:53Z")

</div>

The following slide is from Steven De Keninck’s [presentation](https://youtu.be/2DgxeizE3E8?t=103) at SIBGAPI, an international conference annually promoted by the Special Interest Group on Computer Graphics and Image Processing (CEGRAPI) of the Brazilian Computer Society (SBC).

 ![story_syntax](https://global.discourse-cdn.com/julialang/original/3X/4/7/47ebedd6e2bd80d6f60f9c8f0b94341f7fe06211.png)

The slide shows the differences between the “standard” math syntax and the “standard” programming syntax in geometric algebra equations. The “standard” math syntax is less cluttered and is preferred by mathematicians. In contrast, the “standard” programming syntax is preferred by programmers because the operators are ASCII characters and those operators are relatively easy to overload in most programming languages.

Although I have not yet written a Julia macro, I’m hoping to write one that will offer the best of both syntaxes by translating the unicode operators in the math syntax to ASCII operators in the programming syntax, allowing the geometric algebra equations in a Julia program to be both uncluttered (the input to the macro) and portable (the output of the macro). For example, Julia correctly parses the following equation including the \wedge unicode operator:

```julia
julia> ex1 = Meta.parse("a ∧ b") # \wedge
:(a ∧ b)

julia> dump(ex1)
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol ∧
    2: Symbol a
    3: Symbol b

julia> sizeof.(ex1.args)
3-element Vector{Int64}:
 3
 1
 1

```

For the geometric product, I was hoping that the \thinspace unicode character would similarly be parsed as an operator, but it appears that the default behavior of the Julia parser is to interpret \thinspace as a space:

```julia
julia> ex2 = Meta.parse("a b") # \thinspace
ERROR: Base.Meta.ParseError("extra token \"b\" after end of expression")
Stacktrace:
 [1] #parse#3
   @ .\meta.jl:237 [inlined]
 [2] parse(str::String; raise::Bool, depwarn::Bool)
   @ Base.Meta .\meta.jl:268
 [3] parse(str::String)
   @ Base.Meta .\meta.jl:267
 [4] top-level scope
   @ REPL[122]:1

julia> s2 = Symbol(" ") # \thinspace
Symbol(" ")

julia> sizeof(s2)
3

```

Is there some way to get Julia to parse the \thinspace unicode character as an operator instead of a space?

---

<div class="post-metadata">

**Author:** ![johnmyleswhite](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnmyleswhite/32/31_2.png) [@johnmyleswhite](https://discourse.julialang.org/u/johnmyleswhite)\
**Post date:** [December 15, 2022, 12:59am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/2 "2022-12-15T00:59:46Z")

</div>

Macros don’t change how Julia syntax is parsed. One option is to write a string macro, which allows you to create your own parser. This is how regex works in Julia for example. Given some of syntax your slide implies you want, this may be your only option.

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 15, 2022, 1:40am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/3 "2022-12-15T01:40:09Z")

</div>

> [@johnmyleswhite](#):
>
> One option is to write a string macro, which allows you to create your own parser. This is how regex works in Julia for example. Given some of syntax your slide implies you want, this may be your only option.

Thanks. Do you have a suggestion for a good document for learning about Julia’s string macros? (I had never before even heard of them, although I am familiar with regular expressions from other programming languages.)

The stacked operators in the slide’s math syntax column are probably a bit much: the tilde over a short character like ‘a’ looks ok but over a tall character like ‘b’ is not an improvement … at least not in the fonts I’ve seen it. I think a \tilde preceding the variable would be close enough.

---

<div class="post-metadata">

**Author:** ![johnmyleswhite](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnmyleswhite/32/31_2.png) [@johnmyleswhite](https://discourse.julialang.org/u/johnmyleswhite)\
**Post date:** [December 15, 2022, 1:52am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/4 "2022-12-15T01:52:06Z")

</div>

I’d start with the Julia specific documentation here: [Metaprogramming · The Julia Language](https://docs.julialang.org/en/v1/manual/metaprogramming/#meta-non-standard-string-literals)

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 16, 2022, 1:40am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/5 "2022-12-16T01:40:09Z")

</div>

I wrote this string macro that, for now, only handles the simple case of a single character substitution:

```julia
macro ga_str(s)
	C = collect(s)
	n = length(C)
	for i = 1:n
		if C[i] == ' ' # \thinspace
			C[i] = '*'
		elseif C[i] == '∧'	# \wedge
			C[i] = '^'
		elseif C[i] == '∨'	# \vee
			C[i] = '&'
		elseif C[i] == '·'	# \cdotp
			C[i] = '|'
		end
	end
	return Meta.parse(String(C))
end

```

My initial test tries to calculate these two geometric algebra equations in math syntax:

```julia
axis_z = ga"e1 ∧ e2"
origin = ga"axis_z ∧ e3"

```

From the REPL, the results look correct:

```julia
julia> axis_z = ga"e1 ∧ e2"
16-element Vector{Float32}:
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 ⋮
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0

julia> origin = ga"axis_z ∧ e3"
16-element Vector{Float32}:
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 ⋮
 0.0
 0.0
 0.0
 0.0
 0.0
 1.0
 0.0

```

However, with those same two geometric algebra equations in a program, the result of the first equation is not available for the second:

```julia
julia> include("pga.jl")
utest (generic function with 3 methods)

julia> utest(false)
ERROR: UndefVarError: axis_z not defined
Stacktrace:
 [1] utest(flgOutput::Bool, nD::Int64)      
   @ Main C:\dev\olarth\PGA\pga3d\pga.jl:533
 [2] utest(flgOutput::Bool)
   @ Main C:\dev\olarth\PGA\pga3d\pga.jl:532
 [3] top-level scope
   @ REPL[2]:1

```

I’ll keep investigating and reading Julia’s Metaprogramming doc to try to understand this unexpected behavior of macro return values in a program. If anyone knows the reason off hand, let me know. Also, I’m interested in opinions about the aesthetics of these geometric algebra equations in math syntax:

```julia
axis_z = ga"e1 ∧ e2"
origin = ga"axis_z ∧ e3"

```

Does the ga"…" add more clutter than it removes?

---

<div class="post-metadata">

**Author:** ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)\
**Post date:** [December 16, 2022, 2:17am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/6 "2022-12-16T02:17:21Z")

</div>

I don’t see anything wrong with `gprod(a, b)`, `oprod(a, b)`, `rprod(a, b)`, `iprod(a, b)`, `dual(a)`, and `sprod(a,b)`. Why invent, in my opinion unintuitive, operator notation?

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 16, 2022, 2:45am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/7 "2022-12-16T02:45:56Z")

</div>

> [@PetrKryslUCSD](#):
>
> Why invent, in my opinion unintuitive, operator notation?

When there are just two operands, overloaded operators don’t help much (e.g., 1+2 = add(1,2)). I think the overloaded operators help more when there is a long string of operations (e.g., 1+2+3+4+5 = add(5,add(4,add(3,add(1,2))))).

However, this is all experimental at this point. Based upon the collection of sample projective geometric algebra applications at [https://enkimute.github.io/ganja.js/examples/coffeeshop.html#pga2d\_points\_and\_lines](https://enkimute.github.io/ganja.js/examples/coffeeshop.html#pga2d_points_and_lines), I think a common use case will be projective geometric algebra applications that have a fairly small block of projective geometric algebra equations. I think a good goal is to somehow make that small block of equations as easy as possible to read.

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 16, 2022, 3:09pm UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/8 "2022-12-16T15:09:01Z")

</div>

I’m new to Julia macros and I’m still scratching my head about why a macro would behave differently inside a function. I’ve made the following minimum working example:

```julia
# file: gammwe (geometric algebra macro minimum working example)

# convert GA math syntax to GA programming syntax
macro ga_str(s)
	C = collect(s)
	n = length(C)
	for i = 1:n
		if C[i] == ' ' # \thinspace for geometric product
			C[i] = '*'
		elseif C[i] == '∧'	# \wedge for outer product
			C[i] = '^'
		elseif C[i] == '∨'	# \vee for regressive product
			C[i] = '&'
		elseif C[i] == '·'	# \cdotp for inner product
			C[i] = '|'
		end
	end
	return Meta.parse(String(C))
end

i = ga"2 ∧ 3" # ga macro will translate to i = 2 ^ 3
println("i: $i")
j = ga"i ∧ 2" # ga macro will translate to j = i ^ 2
println("j: $j")

# geometric algebra macro test
# For some reason, within a function, the result i2
# is not available for the calculation of j2.
function gamtest()
	i2 = ga"2 ∧ 3"
	println("i2: $i2")
	j2 = ga"i2 ∧ 2"
	println("j2: $j2")
end

```

The output:

```julia
julia> include("gammwe.jl")
i: 8
j: 64
gamtest (generic function with 1 method)

julia> gamtest()
i2: 8
ERROR: UndefVarError: i2 not defined
Stacktrace:
 [1] gamtest()
   @ Main C:\dev\olarth\PGA\pga3d\gammwe.jl:32
 [2] top-level scope
   @ REPL[2]:1

```

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 16, 2022, 10:37pm UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/9 "2022-12-16T22:37:06Z")

</div>

> [@g2g](#):
>
> I’m still scratching my head about why a macro would behave differently inside a function

After using a lot of @macroexpand and watching Tom Kwong’s [video](https://www.youtube.com/watch?v=JePBb9-ychE) about Julia macro hygiene, it appears that an esc() in the last line of the macro fixes the problem:

```julia
# convert GA math syntax to GA programming syntax
macro ga_str(s)
	C = collect(s)
	n = length(C)
	for i = 1:n
		if C[i] == ' ' # \thinspace for geometric product
			C[i] = '*'
		elseif C[i] == '∧'	# \wedge for outer product
			C[i] = '^'
		elseif C[i] == '∨'	# \vee for regressive product
			C[i] = '&'
		elseif C[i] == '·'	# \cdotp for inner product
			C[i] = '|'
		end
	end
	return esc(Meta.parse(String(C)))
end

```

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [December 16, 2022, 11:01pm UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/10 "2022-12-16T23:01:42Z")

</div>

You probably want to use the tool `MacroTools.postwalk` from the [MacroTools.jl](https://github.com/FluxML/MacroTools.jl) package.

This macro probably does what you want

```julia
julia> using MacroTools

julia> macro geom(ex)
           MacroTools.postwalk(ex) do x
               if x == :* 
                   :geom_mul
               elseif x == :∧
                   :outer_wedge
               elseif x == :&
                   :regressive_product
               elseif x == :|
                   :inner_product
               elseif x == :!
                   :dual
               elseif x == :>>>
                   :sandwich_product
               else
                   x
               end
           end |> esc
       end
@geom (macro with 1 method)

julia> MacroTools.@macroexpand(
       @geom begin 
           x * y
           a ∧ b
           c & s
       end
       ) |> MacroTools.prettify
quote
    geom_mul(x, y)
    outer_wedge(a, b)
    regressive_product(c, s)
end

```

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 16, 2022, 11:09pm UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/11 "2022-12-16T23:09:51Z")

</div>

> [@pdeffebach](#):
>
> You probably want to use the tool `MacroTools`

Thanks for the tip about MacroTools. (I was unaware of that package.)

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 17, 2022, 4:04am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/12 "2022-12-17T04:04:46Z")

</div>

The following macro to give the option of writing geometric algebra equations using “standard” math syntax operators instead of “standard” programming syntax operators is good enough for me for now:

```julia
# convert GA math syntax to GA programming syntax
macro ga_str(str)
	C = collect(str)
	n = length(C)
	for i = 1:n
		if C[i] == ' ' # \thinspace for geometric product
			C[i] = '*'
		elseif C[i] == '∧'	# \wedge for outer product
			C[i] = '^'
		elseif C[i] == '∨'	# \vee for regressive product
			C[i] = '&'
		elseif C[i] == '·'	# \cdotp for inner product
			C[i] = '|'
		elseif C[i] == '\u20f0'	# \asteraccent for dual
			j = i-1
			while j > 0 # shift operator from postfix to prefix
				if isletter(C[j]) || isnumeric(C[j])
					C[j+1] = C[j]
					j -= 1
				else
					break
				end
			end
			C[j+1] = '!' # prefix '!'
		end
	end
	return esc(Meta.parse(String(C)))
end

```

The macro’s translation from math syntax to programming syntax slowed my unit test down by just 2.5% (4.69 us versus 4.58 us, according to @btime utest(false)).

I’m not yet sure if I will implement the translation for the sandwich operator because I am content with the look and speed of the geometric product operator and the tilde (i.e., reverse) operator implementing the sandwich operation.

Next week’s task: integrating the Julia reference implementation of projective geometric algebra with the interactive graphics of Makie.

**Thanks for the help and the feedback.**

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [December 17, 2022, 9:44am UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/13 "2022-12-17T09:44:56Z")

</div>

Is macro really needed here? Operators like `∧` `∨` `·` can be directly defined in Julia. The main issue is with `\thinspace`, but maybe using some kind of multiplication symbol instead of a space would still be fine?

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 17, 2022, 12:24pm UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/14 "2022-12-17T12:24:40Z")

</div>

> [@aplavin](#):
>
> The main issue is with `\thinspace`

The macro isn’t needed: programming syntax can be used if that is the preference. I agree that \thinspace is the main issue. A narrow strip of white space instead of the asterisk significantly unclutters the geometric algebra equations, and I’m hoping that will make them a little easier to read. The use of any symbol for multiplication other than white space would necessarily be more cluttered, although possibly less cluttered than using the asterisk.

The advantage of using programming syntax in the implementation is portability. I think a common use case for jprojective geometric algebra applications will be a tiny block of geometric algebra equations. A goal is to be able just copy that block of projective geometric algebra equations (e.g., from a ganja.js application) into some Julia code and see it work immediately … without any manual translation of operators.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [December 17, 2022, 1:12pm UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/15 "2022-12-17T13:12:06Z")

</div>

> [@g2g](#):
>
> programming syntax can be used if that is the preference

What exactly do you mean by “programming syntax”?  
Some entries from the table in the first post are already valid Julia code without any macros:

```julia
a ∧ b
a ∨ b
a ⋅ b

```

Some can look pretty close, but not equal, to the “math” notation:

```julia
# dual:
!a
# or
~a

```

Only the geometric and sandwich products remain. Maybe, choosing two multiplication-like symbols from [this list](https://github.com/JuliaLang/julia/blob/c1a322fba63da3eda20327d4de9acac389e48414/src/julia-parser.scm#L24) would be cleaner than introducing a string macro?

And anyway, even if a macro is desired, `∧` `∨` `·` don’t need any conversion. Just define these operators directly on your GA vector type.

---

<div class="post-metadata">

**Author:** ![g2g](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@g2g](https://discourse.julialang.org/u/g2g)\
**Post date:** [December 17, 2022, 1:21pm UTC](https://discourse.julialang.org/t/a-macro-for-pretty-geometric-algebra-equations/91664/16 "2022-12-17T13:21:32Z")

</div>

> [@aplavin](#):
>
> What exactly do you mean by “programming syntax”?

I’m using the phrase “programming syntax” as defined in the right column of the initially referenced slide by Steven De Keninck.
