Is it a valid consonant cluster in Lojban?

Pyth, 53 48 47 bytes

!}z+"mz"s.pMs[+VGGc"xcxkcsjz"2*"ptkfcsx""bdgvjz

This generates a list of all invalid pairs based on the rules above, then checks if the input is one of them.

! }                        A not in B
    z                      input
    +
      "mz"                  "mz"
      s                    flattened
        .pM                permutations of each:
            s [               flatten the three-element array:
                +V              Alphabet vectorized concat with itself.
                   G            That is, duplicate letters
                   G
                c"xcxkcsjz"2     That string chopped every 2
                *               outer product of
                  "ptkfcsx"      voiced letters
                  "bdgvjz        and unvoiced letters

Try it here.


Retina, 59 57 54 53 52 bytes

(.)\1|[cjsz]{2}|mz

T`fb-jz`svkv
kx|xk|^v?[kpstx]v?

The trailing linefeed is significant. For valid clusters, this outputs a non-empty string; for invalid ones the output is empty.

Try it online! This tests all clusters at once (removing all the invalid ones and leaving all the valid ones intact). To make that possible I've had to replace the ^ anchor with a \b word boundary.

Another solution for the same byte count:

(.)\1|[cjsz]{2}|mz

T`fk-dbz`scv
cx|xc|^v?[cpstx]v?

Explanation

The goal is to remove all invalid pairs completely. We can do with the valid pairs whatever we want as long as at least one character remains.

(.)\1|[cjsz]{2}|mz

This takes care of three rules: (.)\1 matches any pair violating rule 1. [cjsz]{2} matches any pair violating 3. mz matches the specifically disallowed pair from rule 4.

That leaves only rule two and the other specific pairs xk, kx, xc and cx. We can save a bunch of bytes by doing some preprocessing so we have to handle fewer cases:

T`fb-jz`svkv

The idea is to collapse all voiced consonants into one, as well k and c. I'm also turning f into s out of necessity. This is a transliteration stage which will substitute individual characters for other characters. To see the actual mapping we need to expand the range and remember that the last character of the target list is repeated indefinitely:

fbcdefghijz
svkvvvvvvvv

The initial f => s is necessary, because it overrides the later f => v which would turn f into a voiced consonant. We also see that c is turned into k. And all the voiced consontants bdgjz are turned into v. That leaves ehi... luckily these are either vowels or unused in Lojban. The same could also have been achieved with

T`fcb-jz`skv

Alternatively, check out the other solution I posted above that uses a very different transliteration (with a reverse range, and it also turns k into c instead).

Now the remaining invalid combinations can be checked much more easily:

kx|xk|^v?[kpstx]v?

cx and cx have become kx and xk so we only need to check two cases now. For rule 2, we try to match the entire pair, starting from the beginning with an optional voiced consonant (reduced to v), a mandatory unvoiced consonant (where we don't need to check for f and c separately) and another optional voiced consonant. If the pair is a mix of voiced and unvoiced one of the two optional vs will match and the entire pair is removed. Otherwise, this can only match if the pair starts with a voiced consonant (and has anything else second) - in that case only the first character will be removed, and the other one will remain, still giving a truthy result.