[Question]
  [
Given a string containing some parentheses and some other ASCII printable characters, like this: `(abc((123))(k))` your task is to remove any sets of parentheses that are redundant.
A set of parentheses is redundant if:
1. It encloses another set of matching parentheses, like `ab((123))`, or
2. It encloses the entire string, like `(abc123)`
Parentheses are guaranteed to be matched in the input. Also there will never be an empty pair.
## Test cases
```
{
   "(alpha)":              "alpha",
   "(((([))))":            "[",
   " beta(gamma)":         " beta(gamma)",
   "@lpha((lambda))":      "@lpha(lambda)",
   "(phi((theta)pi))":     "phi((theta)pi)",
   "(sigma)(omega)":       "(sigma)(omega)",
   "(sig(ma)(om)e;a)":     "sig(ma)(om)e;a",
   "(((((a)))b))":         "(a)b",
   "((b(((a)))))":         "b(a)",
   "b((a$c)(def))":        "b((a$c)(def))",
   "x(((y)(z)))":          "x((y)(z))",
   "(foo(bar))baz":        "(foo(bar))baz",
   "foo((bar)baz)":        "foo((bar)baz)",
   "(((((a)(b)))c))":      "((a)(b))c",
   "((c(((b)(a)))))":      "c((b)(a))",
   "(((a))b)c":            "((a)b)c",
   "c(b((a)))":            "c(b(a))",
   "((\"))":               "\"",
   "(('))":                "'"
   "XYZ!\"#$%&\'(*)+,-./": "XYZ!\"#$%&\'(*)+,-./",
   ":;<=>?@[\\]^_`{|}~'" : ":;<=>?@[\\]^_`{|}~'",
   "a((foo(bar))baz)z":    "a((foo(bar))baz)z",
   "a(foo((bar)baz))z":    "a(foo((bar)baz))z",
}
```
  
[Answer]
# Regex (Perl / PCRE / Boost / Python[`regex`](https://bitbucket.org/mrabarnett/mrab-regex/src/hg/)), ~~45~~ ~~44~~ ~~45~~ ~~44~~ 43 bytes
```
s/(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$)/$3/
```
[Try it online!](https://tio.run/##VY7bToNAEIbveQrEFWbqAavxRkrpvS@gsqXuUgokUDaURKW0r46zgInOxc5@/z8nldTFU89a/977zPIigcUSj3FWlcqbm4PSH1zg0EXIXyhTBA/YhRHgGmfIESHwOXYMXfbo9h5rbVvV@b6x@N4i8ueeOTDbnHoQhcoEGnpKiBSGKZNGQCrKkuSVdgEKUcqtIBNUlgM0GZWgyrVwyFMqhKpMUjEijIyJN80F6kSpi0FOREBfwWKEbbIj@iLjG6HVDuyqCqSoqUm0hoaBCNCgY/662JLyr4KUaSfQSoyHtTEJEn83jzdIjI1YHyRGjVtDcuh9fXu/sC7Zlc0dmOH1ze2dazx7C38ZrELO19Hm49idzs4P "Perl 5 – Try It Online") - Perl v5.28.2 / [Attempt This Online!](https://ato.pxeger.com/run?1=VZBNTsMwEIXFNqcowbQzLSUUNogkbfdcAKibYqf5k5ImaoOANOUibLqAW8BB4DSM4yCBF7a_9954Rn59K4J1uv9glXtmP8ZJGoAzxq0f51lhjzqN8v5QhsPLr8HGAg61h_yaTlqTc6xnHuAc-8gRYeJyrBla7MLSJZ82q7rdYp2sSpOvTCJ3ZHcaZoudzuy_DxwQaRELNNSjM6RldGRQCohElpE8VS5AKjK5FGRCEScAZUwRLBIlbJKIgpBnQSQ0gmYM7PZdoEqUKgyyJQK6CuYjLIOQ6ImMZ4RKORDmOUixpiJRGQoaIkCDhvnrYkXKvwQpbU-glug3bX0SJP521jNI9A1fDSS0xs3m6NF-c3t3aB6x4y7vQR8HJ8NTy7iyHXc8mc44n3uL-229e-npX_wB "Perl - Attempt This Online") - Perl v5.36+  
[Try it online!](https://tio.run/##lVRrk9s0FP3uX6E1ZqW79SNp@QKON@3QMLM8CpNSKMRZI8vyg/o1ktMN2aR/fbmytwPboczUM4l0H7rn3KNHynV5pyTPiKcyQilRiwVNPvGj1mLZlz1xFIlIr2SRKNnXXEhGA/9imSQlr4dEdE1f1VLFLIYwXgeaugjokhydSSFNQjvIdtAsSb65@n6VJOCSOYRWlRPmqM1sexZRTYEMpepuSCtvyGovZD9UXcvoD5XWVVsQW9sU1zgZUsFF821IxvXZJX1HyfE48Wv4IEpkt5l5Xz7z4i33Tg6Di0f@4nJ5vQ2o62Tw30D2VfuW11VGMllXTTVIZRs407nc93WXScRyERomYNHt2gHpw1n0xUcq/qo6JN7umlQq0uX/VNZjaT118hg7cfJp/sTMDSS26U9tmjFDb4HenNdahlbe4cZim3pQie7rCmnkQLgmjoDbURRBzs8JhvtOM7uoGr1/9vzV6293tospURSNhT4i@KtWSdEVbXWQGclrjtJT3xE@NfpPmosoogUFp4gGtZMhkVjN8PaRuAhPVn6jsE328ufnq/XatX/6er0ib7Hnh348FnrgKCI1Cckvq/XLqx9fUPgwz45bo1fVVomWA7N7oaSfcvFmUPiXjJraLrG9uW0IPkjDVnZKY1//n/bnFJoZHFSXOYdoFjp1lOPx1YbH1QsI4Ra7Z069QWFr2eIMvPk2ikZ@mKx3KUbQ7c5cbz5qhYVAirIzKbiHh2geWoQYhBDLOX304FI5Cs@Xxl/tEm/umu0ci5zhJMUtf4MlanM4@vBETFliO7Udnj64hwzCOx2wmB2vIf4OR/yWj@G4uWawhQuIAdgyiuHoQOA8Ce4@9VWwRmivRXRlXwq8GT4@EqHcS0GCnVZBWrWBeTbeh@4Yr/uSg2WYbAA/i6Ry4KzgTYPupybKWM2bNOMYZH1ZMTaUmAJ9ZRy6KjCRdY0s@GSyyQYZ3tdluBJSk8zSewsNnHJUj2UyR2uPgb@AHUyE5V3HUq5wET9YxhgtNMBCMv@OwgE9DzLQc4/JEBLECCvQkcJ75IlDCsIShhCffLE9DnjIrde//X5mf@Z8fh5TdgGPXM8PrK/CRXS5fLqJ4@118sft8fSO/g0 "Bash – Try It Online") - PCRE1  
[Try it online!](https://tio.run/##fVNrc9w0FP3uX6EYEemm3lfKF3CcbaddZsKjMFsKhfXGyLL8oH5oJG8TNrv96@HKDgPpUDxjW/feo3vOuZZ1qe8vlrrUhBoSEW1UkRilayEVZ7Pp2TJJSlH3iewaXdXKxDyGMF7PLAsIwzvHZFIoB2h71faWJ8nXV9@tkgQCsoDQq3LCqdnMtycRswxIX5ruhrTqhqxupdJ91bWcfV9ZW7UF8a3PcA/NUApuWmxDMuzPLtkHRg6HUV8jelmius188uXzSbwVkyPlcPZkenG5vN7OWEAz@G8i/6p9L@oqI5mqq6bqlfEdnXOubnXdZQq5AqSGkVh2u7ZH@XASffGJjr@YDoW3uyZVhnT5P53t0NqOTs7RCc3H9VO3dpRoczradO8MswVmc1FbFXp5Z5RAm7Y3idV1hTJyIMISKuFuGIokp6cEy7qz3C@qxt4@f/nm7Tc7P0BIFEVDo08M/E1rlOyKttqrjOS1wNGzKZVT5uY/zlxGESsY0CLqzU6FRGE3p3uKwmV49PIbgzb5659ertbrwP/xxXp1Tt6j6ccFPBe2FzhF5hDJz6v166sfXjH4GOfHrRtY1VaJVT33tTRqmgr5rjf4SIah@gHxJwvfKXwEQy87Y9HY/8P@GEtzx4Pj5XQfzUNaRzmeX@t0XL2CEO7QPqf1BidbqxZXMFlso2jQh2C7S7GC6WAeTBbDsLARKFl2DoIfcR8tQo8QxxBiO6qjR38VNXjALN51QCaLwH3PockJLlL85u@wRe1Ohw6PxLUlPq398PjRj8ghvLczHvPDNcTf4huv5TkcNtcctnAGMQBfRjEcKMzo09n9PRe1LgV4DrgBvDySql7wQjQNpp@5Kue1aNJMYJHrsuK8LxECunIJWxUI5F2jCjGGfIxBhQ99Oe6E1IF5@hBhgEuB5nimcoxusfAn8L2r8LzreCoMbhJ7zwVDhAF4KObfVdhj5hECMw@cHClBDrQSEyn8zTxqSEF60gkSYy72hxeeQe/tr7@d@J/Rz09jxs/gSTCZzryvwovocvlsE8fb6@T3u8PxA/sL "PHP – Try It Online") - PCRE2 v10.33  
[Try it online!](https://tio.run/##fVXbcts2EH3nV0CMGgISbfnStC4h2pnM9CGTTB96mUlq2ixIgRQmvA1B1ZJM5dOrLgBKptSoGI2ovZzdg93lKq6qszSOt69EEWeLGUfTqCxlM6l5ypfn86q6teI5q1FY3T8gH/1qv3t6@hJdLz6vVp9@n//wNMdbOcEBbh9J8AGecO6uSHv/iMkDGZGAEHznB6QdksnwerIlx3DbRaPKDytqvTAQQKDmLL@1RNGgqobvkNd1WeO4LGSDNKFRzqVkKSfPspl5XgwOaDpFnVb91HpezDKKat4s6gJd0g1SIeWqaNiyi0meO2s/kf2bdkFasgndmCrMeCZy/4IiKFIGoWpWpBxrU@HqBzOPaB91AUFvwgYXZ4ygqb@XI5B3YUeJKGahDm6CjSRBzxaCk5Q1wpTK8XinUUckCA@UU5fklz8@fqR7K88k1y4j6ftOEDgANYjx@Aiz@TZIU9l7ShN6Y210Q3ImCrxjoxHjcTXwHem07QCbEikVeQlwUG@qQdpvetG2uypqhfv9jfvmhhxp2@sr96cf3curN2CB2Yjn0KEhJqPx@fT27tF2Dd99voNOvi/@ZpmYmd6Jhqt2IqBnLtWVG0a714Oq4ziQJ68wgo5AbS@cfpjkMIzchUlOh0kOwqjhuL4KG1RCJP0iep5@Ez2vKMO8nIU5RWzRlEj2PGBGctaEiahlE5ZFtkLtkYllGe2NE6SFfgWFQ1GiBks@iSaeQyOTXVtjBtPgCMdDQKX10REZoezUDE4Er@oX2kPJUyjFX55CLf8PtTyFyg3qtY@@nirXN1ApoOQB6j8lpH3UjCdskTUeOlg19s@qix7aDViSsRTZygKDcGr/GBZ6jfUJgweuXFTCSGgYDLkoUtgw1aLpdU69fmu1f7RTyptMFBwbVqJwjT@hx6tiTRDo1Yxi1XVIsvYvX/ZFt2P6KHX6RMpFczSRYc2rjMUc65wuXMBFEj5whYMoKr9G@x27fmH3TsoCCcrdbc2@OWASqyhQ1l5NjEdX3AtqbbaYZdWcEUv9D90TOBaKeMNwyvIc1G@VFeOM5dGMgRFXc4FxMwcXUgmlkCIFR1zmPGVGxEYmnHZxMSBJpJxx1EkgwE82jAksrASkJRhWBAqv3JKyxBGrAcTWlhK0BAKxgEzfStagOfAATZcTQ0oS67QxKCKyy2w4RCS2YkWIGV1g64cD358@/zmwXw2/ex04eETG7tn5xPLo1L@9e3sfBA@P4V/P7ear80@sJlhuzzLd51C3@V8 "C++ (gcc) – Try It Online") - Boost  
[Try it online!](https://tio.run/##bVJrU9pAFP2eX7GmtNmLkYdoq2BE2zIdpqgzap8JtBuyCTvdPGY3tIDYv05vEhnrjPmQ5Jz7OOfu3WyZz9KksxFxlqqc6KW2FY/4okeUo0zT3Ogm9eh6At5H/OLT34e1O6Ewhjp4ALTveLCuQdPrNDeY77a7e@2xIUKi3NZ4x7G01SWKCc3JzTLJ2WKgVKqodSG0FklETG1aYASOcquqVAU0ACISonnemCciTSj@UcWSiNODI/vwCMB@ZI7f2O39wyfU60P7uIPMXT1mGcWGtlmjUN9tnJz2Jybcw7OGhslvJkVAAi5FLHKu0JYiDlENnUmRo6nCnuQJVbDjHDzb44tKcaRkHvtckTR87KWxWShZpJ1Wj0zTeZI7bSNMFQmLSZXbGXcNgu1Dx7EiPLAqpWUQLjXvkrKUrB1yZwmrW@6nMfxweXU9eHd@M7CJFW/Zi0@j2@FoeFmQeku@v7o9H42QWWyZz4Prt1c3g3s3HBsaz35/XO4bd9AjK9QtrMlyCUvd0HkgkspfMb4EwpKASBf3jHa9BP1KR7qtcvHkz0xITtqYT@Li@Eo9Pffx3GyibSLtajq7mgp6KOSQGC8LVqBE7BZtW13iK85@laqrLsmUSHIKBrpr9x4QTwJHwoYymc0YGMXldAEfg/g8ZzRicYz0WRGlVLLYDxgGaTYTlOYzTIFMFIQWESbSNOYRqyCtMPDeQ1@KleAXydR/QAjwl9WmQAMeIlpgYAl0VURomKbUZwqL2MooQIkQgIFm/o/CCpknGcg8aFKUhGkpO0XCh61y5cGHqTEtDLGK88zyY@H767fvO@aL2stXnkXrsGvvNZpGt3finPbPXM8bT378vFvf/7X@AQ "Python 3 – Try It Online") - Python `import regex`
This is a single regex substitution to be repeatedly applied until it has nothing to match. However in Boost, it is applied until there is no change (since its substitution interface apparently doesn't allow detecting whether any replacements were done).
This golf down from the 45 byte version is similar to [Neil's .NET regex](https://codegolf.stackexchange.com/a/250664/17216), in that it is flanked with `(?<=\(|^)` and `(?=\)|$)` (or equivalent). When initially writing a PCRE regex answer to this challenge, the thought occurred to me to do this, but I dismissed the idea, assuming that empty capture groups would need to be used to XNOR the flanking conditions (i.e. force them to only match if they agree on `^` and `$` or `\(` and `\)`), by doing `(?<=\(()|^)`...`(?=(?(1)\)|$))` or at least `(?<=\(()|^)`...`(?=\1\)|$)`.
Then later on, after Neil posted his answer, I tried porting that approach to mine anyway. I found it to indeed return some incorrect results unless forced to agree using an empty capture group, but at that point I'd already gone with the (as it turned out incorrect) golf of `[^)]` in my regex. As it turns out, with `[^()]`, the flanking approach is fully robust in recursive versions of the regex, without requiring any additional XNORing logic or atomic grouping.
```
s/                 # Begin substitution - match the following:
(\(|^)             # Assert either we're at the start of the string, or there
                   # is a (redundant) left-adjacent opening parenthesis.
\K                 # Keep everything matched up to this point out of the match.
                   # This is a more efficient way of doing the same thing as
                   # lookbehind in many situations.
(                  # Define recursive subroutine (?2)
    \(             # Match an opening parenthesis
    (              # At the outermost level, $3 = the following capture, i.e.
                   # with the parentheses matched outside it discarded.
        (
            (?2)   # Call (?2) recursively
        |          # or
            [^()]  # Match any character other than a parenthesis.
                   # It's not safe to reduce this to "[^)]" because if,
                   # after popping out to the top level and exiting this
                   # loop, the "\)" fails to match because there's more
                   # inside the top-level parentheses pair matched in this
                   # regex, it will backtrack and can incorrectly match an
                   # opening parenthesis here.
        )*         # Iterate the above as many times as possible, min 0
    )
    \)             # Match a closing parenthesis
)
(?=\)|$)           # Assert that either we're at the end of the string, or
                   # there is a redundant right-adjacent closing parenthesis.
/                  # Substitution - replace with the following:
$3
/                  # End substitution - no flags used
```
I also intend to write a single-use `s/`...`//g` substitution that will erase all the parentheses in one go, but that will be significantly more complicated and less efficient (as it will need to emulate variable-length lookbehind to erase the closing parentheses). As such, it would be demonstrable in [regex101](https://regex101.com/r/UsM7Nv/1), which can only apply a substitution once. (I've done this for .NET, but intend to for PCRE2 as well.)
# Regex (PCRE / Ruby), ~~48~~ ~~49~~ ~~45~~ 44 bytes
```
s/(\(|^)\K(\(((\g<2>|[^()])*)\))(?=\)|$)/\3/
```
[Try it online!](https://tio.run/##lVRpj9s2EP2uX8FV1CVno8NO@iWVZSdoXGB7pIWTtGktr0pR1NHIkkDKWddr569vh9IG6QZNgQiwyTk4783jkXJd3irJM@KpjFBK1GxGky/8qDVbdGVHHEUi0ilZJEp2NReS0cC/WCRJyes@Ee22q2qpYhZDGK8CTV0EdEmOzqSQJqHpZdNrliTfXf64TBJwyRRCq8oJc9R6sjmLqKZA@lK116SR12S5F7Lrq7Zh9KdK66opiK1timucDKngoukmJMP6bE7fU3I8jvy2vBclsltPvCfPvHjDvZPD4OKhP5svrjYBdZ0M/hvIvmze8brKSCbralv1UtkGznQu913dZhKxXISGEVi0u6ZH@nAWff2Zir@pFok3u20qFWnzj5X1UFqPnTzCTpx8nD82cwOJbfpjm2bM0FugN@e1lqGVt7ix2KbuVaK7ukIaORCuiSPgZhBFkPNzguGu1cwuqq3eP3v@@s33O9vFlCiKhkKfEfx1o6Roi6Y6yIzkNUfpqe8Inxr9R81FFNGCglNEvdrJkEisZnj7SFyEJyu/Vtgme/nq@XK1cu1fvl0tyTvs@b4fj4XuOYpITULy63L18vLnFxQ@zbPjxuhVNVWiZc/sTijpp1y87RX@JYOmtktsb2obgvfSsJWd0tjX/6f9NYYmBgfVZc4hmoROHeV4fLXhcfkCQrjB7plTr1HYWjY4A2@6iaKBHybrXYoRdLsT15sOWmEhkKJsTQru4SGahhYhBiHEck4X3btUjsLzpfFXu8SbumY7hyJnOElxy99iidocji48EVOW2E5th6dP7iGD8FYHLGbHK4h/wJGxuJg9mh/XVww2cAExAFtEMRwdCOLHwe2XPgvWgO01CK/sucCr4eMrEcq9FCTYaRWkVROYd@ND6Jbxuis5WEiFrQE/i6Sy56zg2y26n5ooYzXfphnHIOvKirG@xBToKuPQVYGJrN3Kgo8mG22Q4V1dhishNcksvbPQwClH@Vgmc7T2GPgb2MFEWN62LOUKF/GDZYzBQgMsJPPvKBzQcy8DPXeYDCFBDLACHSl8QB45pCAsYQjx0Rfbw4Cn3Hrz@x9n9gPnq/OYsgt46Hp@YH0TzqL54uk6jjdXyZ83x9N7@g8 "Bash – Try It Online") - PCRE1  
[Try it online!](https://tio.run/##fVPbcts2EH3nV8Asa@w61M3pS0vTSiZRZ9xL2lGaNq0osyAIXhreBqBiV5by6@6CdKd1pqlmKGJ3D/acswC7oru/WHZFxzzNQtZplcdadZWQCvhseraM40JUfSzbuisrpSOIMIjWM8N9xunJKBnnygKaXjW9gTj@@uq7VRyjzxYYOGXGwNOb@fYk5IYj6wvd3rBG3bDVrVRdX7YN8O9LY8omZ65xOe3xUpJCmxbbgA3700v@gbPDYdRXi14WpG4zn3z5fBJtxeToAZ49mV5cLq@3M@57Kf43kXvVvBdVmbJUVWVd9kq7ls46V7dd1aaKuHyixpFYtrumJ/l4En7xiY6/6JaEN7s6UZq12T@dzdDajE7OyYmXjeundm0pyeZ0tGnfKWVzymaiMipwslYrQTZNr2PTVSXJyJAJwzyJd8NQJDs9ZVTuWgNuXtbm9vnLN2@/2bk@QcIwHBp9YuBvGq1kmzflXqUsqwSNnk89OeV2/uPMZRjynKOXh73eqYAp6mZ1T0m4DI5OdqPJJrz@6eVqvfbdH1@sV@fsPZl@XKB7YXpBU@QWEf@8Wr@@@uEVx49xbtTYgZVNGRvVg9tJraaJkO96TX/xMFTXZ@5k4VqFj2DkZacNGft/2B9jaW55aLzg7cN54FVhRvfXWB1XrzDAO7IPXrWhyVaqoRVOFtswHPQR2OwSqlDan/uTxTAsaoRKFq2F0CHuw0XgMGYZAmrndeGjr8rTdMEMPZXPJgvfnufQ5IQWCZ35O2pR2dvRBUdm2zLXq9zg@NGHCBjcmxlEcLjG6Ft6A0T5xfnlYXMNuMUzjBBhGUZ48HAWPZ3d34OoukKgQ0jYIP0clqheQC7qmtLPbBWgEnWSCipCV5QAfUEQ7EqbMGVOQGhrlYsxhDFGFTz0BdqJiQVD8hBRQEtB7iBVGUW3VPgTYW8rkLUtJELTJrF3bDBEFKBDYv5dxT1lHiEo88AJRIlyoJWUSPBv5lFDgtKRVpAYc5E7vOgSOm9//e3E/cz7/DTicIZP/Ml05nwVXISXy2ebKNpex7/fHY4f@F8 "PHP – Try It Online") - PCRE2  
**[Try it online!](https://tio.run/##bVFtc5pAEP7Or1iNLXcJokn6pRo0TuN0nCZpx6TTtID2gONlBpDeYX3D/HW7qLFJJnzg7nn22Wf3dsXUWWwEGHDVu@/pgjPvRAW1DZEPwmzaFUOViASLJIe7RZqzeV@IidBAvYmkjNIAqrKKCp56ioc2wjy1t9leR31UoSjAMx4bZrP@sVe3bFZf1wg9PtEvOt2R3XjbeJD@ZXHkgcfjKIlyLvb2ZZdCl1kc5cSjuxb1mKdBHkLFgA9vu/0QE2wynSYOFzDx/7vKva2Pts02BBLPekdq2UrqcuqQTBPmmU3XaGue2zpnbjh2QyZgBYVbgMuwlKsAzEKeAs7scI3Q2IfCgCEP@DxrtQafb78O@596d/2DZv5a03@4799e9a8OiuS14ub79f3genDbBzgCyRIOTMI3LmJVAi4J/JgFQIa4TyT4n2mEU@RpXr75SZWoEElg8YwtJExSeigWqM8HoAkcQfByBjiptTKEp3b0lM@2u9b8ss0ykWCm0aSrzDAkdEFCC3wzkKbUYGhrIG10WRo46VkYxRxiI@C5xA5wjctKyWfTXG4LlbrTXSQ266e2YVSttNrGlNhs6nr9zN5tDiATEb7QN@M9Mx7jEMfjjWwQixQjan3BkxAruDjrFOaIUJseU4tS0jUsWtRowzpvbDaExVnIqIJKYlL8FHB4zkjAkgTpyzJKSMwSx2MYJFkYEZKHKKFZVBIyClBIJgkP2A6SHaa8vfclmEmdUkycPUKAV1ZzKfG4j2iOgQUlyzJC/MmEOExgElsqJdgiBFTBZp5H6RKZFwpk9jUJlqTutqyLhEOfKu96cKiruGVDbMdZ1e2h4v/h569K9aj27r2lkmN6otX1htJqXxid7qVpWfZo/HtVrB/Vfw "Ruby – Try It Online") - Ruby**
As of 45 bytes and smaller, this is an absolutely straight port of the regex above. Previously, it needed to work around the difference in Ruby subroutine capture group behavior.
## \$\large\textit{Functions}\$
# [Ruby](https://www.ruby-lang.org/), ~~75~~ ~~76~~ ~~74~~ ~~72~~ ~~71~~ ~~66~~ 62 bytes
*-5 bytes thanks to Steffan*  
*-4 bytes [thanks to Dingus](https://codegolf.stackexchange.com/questions/250861/add-parentheses-to-polish-notation/250875#comment559072_250875)*
```
->s{0while s[/(\(|^)\K(\(((\g<2>|[^()])*)\))(?=\)|$)/]&&=$3;s}
```
[Try it online!](https://tio.run/##VY7dUoMwEIXveYoUsd2t0j/vTNP23hdQCdWE8jcTWqbQ0VL01XGBOqO5yOZ852x2jyd9biLRuKviMvtIUhOywpuChHqL8okqgIyXi1XtbQF9HKNEhLWQWDs49YdD4Tzw4qupxIyzvt2IOCwLztKIVQPC@YlUuN/xSsw7ajx37gthy73NjTDebDJxF34XYfkx3Zcs8ozPGYEGlMkThRatAR7SsZgOSwWxyjLCm9YFMCrTO0Um5EkKUCYUwTxtQZHGFIRDFsaql9BrDPn1X6BO1G0Y9FWRoKdyAoRdGJH6JOOMULUORIcDaHWkJlVZregUCbRomb8uVkT@JYhcZwKNxKAbGxDQ@Du530FjYAXtQqpn0u7KiO7nl9eBfePcDuUIxnh3706m1iNfitV640npb9/eL/XX9@gH "Ruby – Try It Online")
# [Julia](http://julialang.org/) v1.6.1+, ~~84~~ ~~85~~ ~~84~~ 83 bytes
[Attempt This Online!](https://ato.pxeger.com/run?1=PVBLTsMwEBXbnIClCYXOlBYQbFCD2-5ZIiSgbsFJ7NTI-ShJJQiBi7DppoeC0zBJCrOY8fvMs-Wv7cvaGrnZbNelHl1932oohhk_R15wnrEpK9iYachVZmWgSMtdEFAvUdzQpJpeYD1fAi5wgAIRplxg3UOXTwpXXLo4LHAXfVdxLW2hPKbTnFlmEpYrGVqTqALQYSyyqS8tqzyjqWW5SUqbAHoqCT1W8TJfK3K1PGiwiA4pXfjmZ28fpM1WEp3mVXOkcpivSgmRjGOiZ40KYGXsh5JEyFYGoFyRBTPTEIWJyAhprCLZQegwKm-XC7SJfmMGf4cI0FH2AoRQaUKvJLwhVI0COk3BlzktycppQIsI_OcBxWHQRgZE-PiX2uX7GDhBc5nsOOG2o0_9_uHxwD3sHR2LPgzwZDg6PXPG3jWfTGdzIRbLp-f3-uOz3_3QLw "Julia - Attempt This Online") (now the same as below)
# [Julia](http://julialang.org/) v0.7+, ~~86~~ ~~87~~ ~~84~~ 83 bytes
```
f(s,p=0)=s==p ? s : f(replace(s,r"(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$)"=>s"\3"),s)
```
[Try it online!](https://tio.run/##PY7dUoMwEIXveYqI1e7W@n9XTOm9L6A2rSaQ0DjhZ0g7UxF9dVxAzcVuvnN2T/J@cFbeHrvOgJ9X/Aa557xiMfNswQzUunIy0eTVIQhotygeqdOJ77BdbwE3OEOBCDEX2E4w5EsfivsQ5x67hhvpvI6YKWvmmC1YrWXqbKE9YMBY5kolHWsia6hUtS32rgCMdJFGrOH7@qBpatDBgEMMyOlAumonMeh/sUY6AVN6LyGTeU7yqncBnMxVKsmEamcB9jsawcr2grcZDUKZ60yOCCOjjn5zgTZR9cOgfomArnKSIKTaEB3J@EBoegdMWYKSNS3JJuhhIIL/PKA4TIbIhASFf6ljvsIkSPrH5KiJcGhTqk/PLyfh6eTsXExhhhfzy6vrYBE98GW8Wgux2b6@fbZf39Mf "Julia 1.0 – Try It Online")
Julia's implementation of PCRE substitution appears to be deficient, not actually using `pcre2_substitute()` (the giveaway is that `$1` `$2` etc. capture group syntax is not supported). This is a shame, because PCRE2 has some advanced conditional replacement features (enabled by `PCRE2_SUBSTITUTE_EXTENDED` in its C interface). The explanation, though, is that it was originally integrated with PCRE1, which has no built-in substitution API – so presumably when they switched to PCRE2, they kept the substitution code they'd already written for use with PCRE1.
# [R](https://www.r-project.org/) v4.1.0+, ~~90~~ ~~91~~ ~~89~~ 88 bytes
```
f=\(s,p=0)if(p==s)s else f(sub(r'{(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$)}','\\3',s,,1),s)
```
[Attempt This Online!](https://ato.pxeger.com/run?1=VVBNc9MwFByu_hVuCNXb4kILFwZXpHdypxA1IDuSoxnH9vg5M5CP_hEu4cCPgl_DcxxmWh0k7b59b1f6-as9HH6vO3_57s-d14Y4afQVgqdGawbHrmQXe-J1Rq3akqHdHOajnLImb7CbzQn3uIABaKINdmPsVaKMeasSTpJrJIzT_IL1VRpvtA-loxF3i1CNkMa-bmkahyrmddO0jvmTbatQFUyts4tpqBzTBsBWQjFy25EylULK-jqNe-hpCuzlXtbsRDrYHf4-uyFbNkuLqA87kxGI4sx1lgq7Wgl921eJSrvKFlaK1CwDUbcUCZrQExwKEVK9coUdIA0YLj3NJelE1ospOyEBcrXjHLRwXtB3KfzA8RUR-bqmzLbSZDdRD45IACIJ87iKjTBPFMKcPEkskR9tcyEy_HceMmTIo7wPZAfOjI6Hkv3u85ez0fPxi3Oj6AIvk8tXr6P36Y3-MLmdGXM___ptu9s_qOEX_wE "R - Attempt This Online")
R is apparently also not using PCRE2's built-in substitution engine, otherwise `$2` would work as the replacement argument.
# [Python](https://docs.python.org/) (with [`regex`](https://bitbucket.org/mrabarnett/mrab-regex/src/hg/)), ~~123~~ ~~108~~ ~~107~~ ~~106~~ ~~107~~ ~~106~~ 105 bytes
*-15 bytes thanks to Steffan*
```
import regex
f=lambda s,p=0:s==p and s or f(regex.sub('(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$)',r'\3',s),s)
```
[Try it online!](https://tio.run/##VU/bUsIwEH3PV0REs4sFQd5aI7z7A2oDmt4709s0dQYq@uu4vTCjmUySc9k9m@rYJGWxPp/TvCrrhtdhHB5YJDOde4Hmxqrk0jZSVlwXATe8rHkEvWlhPj0QoOC0R/VMN63NA57cPeAOZ6gQYSMVnqYorFqotbAM0r4kmaNxeCuXLKKeGU@LjlmYJkgLm/E04llYQIZ9bubOVzsphSqEzTOZuUubiN7V2ryq06IBZNRt5YwoLAIZUTmeQWdVopF187lIi3EvbDTEOs@J3nYqwPBfEqFKUoAmIQtWaUeYNCYjlHkY6wHCgDF0xr5Aleh1ZvBGRICeeuojBGFE6EDCEaHtFIjKEjxdU5FuWQd6RAAZDfNXxZaYfw5ixkygSPT7WJ8IDy/Jwwwe@szvBtIDpyb9Jeh8eX27mlxPb26VgBneWfPFPbOdR/m02bpK7fbvH1@n7x/xCw "Python 3 – Try It Online")
For golf reasons, this continues substituting until the string is unchanged, not until there are no matches (with this regex either of those two methods will work).
Alternative 105 bytes, using essentially the same technique as [suggested by VisualMelon](https://codegolf.stackexchange.com/questions/250596/remove-redundant-parentheses/250617#comment558588_250617):
```
import regex
def f(s):
 for i in s:s=regex.sub('(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$)',r'\3',s)
 return s
```
[Try it online!](https://tio.run/##VVDbUsIwEH3PV0REs4uIIG/FCO/@gNqAplcy06adpsxARX8dty3MaB6yOZfds5PyUG8LOz@dTF4WVc2rOI33LIoTnoBDj/GkqLjhxnLnOdmpE7cLQICC4wbVC1U6y0c8@hvANY5QIcJSKjwOUYwroeZi7JDR5HpX0ZhLkju4BW/klLUJWZdwcBNXR8ZSrEl4FlvIkGsb8cy/n62lFMoKj2cy86ceEZ2r8XhZGVsDRTRytjij2EYyoXY8gc7KrUbWrukjHcaDuNaQ6jwnetWqAJnOg0iTCOXWANRbsmBpWsKZlIxQ5HGqewg9xnhxngvUiUFrhuCMCNBTD0ME@k1CexIOCE2rQFIUEOiKmnTDWtAhAshomb8qNsT8cxBzzgSKxLCLDYkI8JLc7xBgyMJ2Id1zatAVQffr2/vV4Hp4c6sEjPBufD95YN7iST4vV75S683H59fx@0f8Ag "Python 3 – Try It Online")
# [Python 3.8+](https://docs.python.org/3.8/) (with [`regex`](https://bitbucket.org/mrabarnett/mrab-regex/src/hg/)), ~~100~~ ~~101~~ ~~100~~ 99 bytes
```
lambda s:[s:=regex.sub('(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$)',r'\3',s)for i in s][-1]
import regex
```
Can't [Try it online!](https://tio.run/##VY/dUsIwEIXv8xQR0ewiIMiNU4xw7wuoDWj6B5np3zR1Bir66rhp64zmosl39uzZbXms90W@uC@rcyLVOdVZEGluPd96sop38WFqPwIQoOC0RfVEN53VHZ78LeAGR6gQYSUVnoYoxpVQCzG2mBQVN9zk3G78yXzDTFYWVc3bwHMP9miXvJEz5sxpaz7aqa0jk3uMm4SncQ4pcp1HPHUpUgqVC4@nMvVnnot1rsbjZWXyGpBR2nzZU5xHMqF2PINOy71G5hb3kQ7jQVxr2OksI3ntqgDdj1MRyr0BqPdkwdI4wZodGaHI4p3uEDrGeNnnAnVi4MwQ9ERATz0MEaI4ITpQ4YjQuAokRQGBrqhJN8xBSwTIaJm/VWxI@ecgpZ8JNBLDdmxIQoC/k7sdAgxZ6BbSnaYG7SXo@/zyejG4HF5dKwEjvBlPprfMWz7Ix9XaV2qzfXv/PH19ix8 "Python 3.8 (pre-release) – Try It Online") - Confirmed to work on my machine, but `regex` is not installed on TIO or ATO.
Uses essentially the same technique as [suggested by VisualMelon](https://codegolf.stackexchange.com/questions/250596/remove-redundant-parentheses/250617#comment558588_250617).
# [PHP](https://php.net/), ~~108~~ ~~109~~ ~~108~~ ~~107~~ ~~106~~ 104 bytes
*-1 bytes (→ 106) [thanks to Steffan](https://codegolf.stackexchange.com/questions/250861/add-parentheses-to-polish-notation/250873?noredirect=1#comment559241_250875) with the added bonus of now being an anonymous function*  
*-2 bytes (→ 104) with no "Undefined variable" warning*
```
function($s){while($p!=$s=preg_replace('/(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$)/','$3',$p=$s));return$s;}
```
[Try it online!](https://tio.run/##VU/LjtMwFN3nK9JgyL1DSqewq8dkFlRohDSDCgugboOTcR6S21hOKqDt8OvlOi0SeGH7PHzPsa3t6Sa1tQ1ZKU7lblv0TbsF1uHhR90YDcyOBOuEdbrKnLZGFRriCUg4rlF@oJNW@hqPyzXgCq9QIkIqJB4ZTuIkZm/ihFmagMid7nduyzr@dOJl64A14pozI8pK9x18@vzu7h45HsKmBGaWXe@MpiYGx9OVEJHcRkjmbpeTQnRynYynyAd3g7qoW2/hIU2d8tDjcChtVNdn2jkKxJH4uJi/z@4fsvli8bBIo7nnZ2E0Yz4TqRkoY2uFgf/XEmkFYa57BZXabIi@9SqAUZv8UZEItm4A@posaBtPdE1FRmg3ulJnCGeMml/mAr3E3JshvyACdFWsQHjUJaGfJPxC2HsFyraFXDl6pPaBBwMigAGV@VfFPTH/OYi5ZAJFYjHEFkTk@Df53CHHIih8IXXmZDQcMe1fvn4bRc/Y8xcyhit8mYxfTYIZvxFv09ullKt19v1wfPod/wE "PHP – Try It Online")
## \$\large\textit{Full programs}\$
# [Perl](https://www.perl.org/) `-p`, ~~52~~ ~~51~~ ~~50~~ ~~51~~ ~~50~~ 49 bytes
*-1 byte thanks to dingledooper
and [Sisyphus](https://codegolf.stackexchange.com/questions/250556/transform-characters-of-your-choice-into-hello-world#comment558485_250565)*
```
1while s;(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$);$3
```
[Attempt This Online!](https://ato.pxeger.com/run?1=VVDNToNAEI7XfYqKKDNVNNWLkVZ69wVUFnSXLj8JBNJiVMS-iJcetO9kn8ZZFhOdw85-fzuT_fis1bLYBpZbW-Hm66lJ3MvvyeQ5yws1WnnAoYuQ31Cn8s-xCyLAEMfIEcGfcexs9OwLExzym93eFERRZwKZjgVIxUZSNQJSUZZEz7UKUIhSLgSJUGc5QJORBetcE6s8JSNUpUqFgWAwKm94FyiJUptBDogAXYUdIyxUQuiFhFeEViuQVBVIsaSQaJkGPSKAjJb5q2JLzD8HMcNMoJEY92NjIiT-TjY7SIxZrBcShuNW3xw6b-_u960D-_CIOzDG4xP39IxdedPZtT8POA-jh8e37n3tmF_8AQ "Perl - Attempt This Online")
# [PHP](https://php.net/) `-F`, ~~103~~ ~~104~~ ~~103~~ ~~102~~ ~~100~~ 98 bytes
*-2 bytes (→ 100) [thanks to Steffan](https://codegolf.stackexchange.com/questions/250861/add-parentheses-to-polish-notation/250873?noredirect=1#comment559241_250875)*  
*-2 bytes (→ 98) with the added bonus of getting rid of the "Undefined variable" warning*
```
<?for($s=$argn;$p!=$s=preg_replace('/(\(|^)\K(\((((?2)|[^()])*)\))(?=\)|$)/','$3',$p=$s););echo$s;
```
[Try it online!](https://tio.run/##VVBNj5swED2XXzFL6XpmG4ra3GoIe1op6mEPvbQNSWrAASSCLUBqy7L96@k4yUrdOTB@H543OFdDfeq1KiHsSxAC@jgW@2uJU5weTI/BkASqrzoZ2JuEge11te@1bVWhUUSY4byj7At3rvQTzZsd0pbuKCPCNMloDigSCxEsxSKwPIEkSV3UJhjk6SXMcwSEHfhx@pSugt4Xz47ys86XAlZQmFJLwEKNEpxAzK0lDHqE0IBtrD6oppUQGTtGtrZR3nSuQ1hApMcCwhKG2vTj3ljd7UdVJY8dhA/nwRDDGmb4VTethvXD1wSujwJKQmm8NxsIJwgm2M6zC5dT8lG@NrYQ3y4vq51/I1C@V5pOw5Jnn1C1tlbkuSfaEJcHuR4VVup4ZPreqYitOualYhFt3SCONVvINo4YmoqNaI66UheIF0xaXuci36TcmTG/IgZ8VEFBWOoDo98s/CGcnIIHYzBXPV9Sk@fAGTEgj5f5X6WJmVcOZq6ZyJFUnGMLJnJ6Sb7skFPhFW4hdeEy/9wEf799/3Hjvw3e3WYC7@j9IvwQeZ9lnKzS@02WbXf7n0/z81/xDw "Bash – Try It Online")
[Answer]
# [sed 4.2.2](https://www.gnu.org/software/sed/), 98 bytes
```
:a
s/(\(([^()]*)\))/\1/
ta
s/\(.\)(\([^()]*\))\(.\)/\1\t\2\n\3/
ta
s/^(\([^()]*\))$/\1/
y/\t\n/()/
```
[Try it online!](https://tio.run/##TY3NUoNAEITv@xQaUXqiZku9Jf7kMdRM0FkgQFUIlHAw8efVcRbQcg672/319DZp0nVzMY0FA6sItJ4SE1m@sqb1PmPGpHBginpDObd8zTu@GXPR/0zQ7@@tZnYWZLsOsq1zIQOdFemYI5e2gkzKUu2lp8BWSpeIQtR5AbS5RqguvNEUmQZRlWkmg8SgKV2MvdBNcj4MNyoV@pQgJiTpRtW7gj3h4Ak2VQUnb7okB@NFr1T89UHrKO4rYzUc/bYO/Y5iE/vPZPB40l@hno9Pz8c8OQlOzzjElM4vLmfWzBe3d/cPyxXzOnp5/fj8@g5/AA "sed 4.2.2 – Try It Online")
*-a handful of bytes thanks to Jiří for reminding me to use `y`.*
*+4 bytes thanks to Deadcode for noting that any printable ascii could be in the input string*
## how
```
:a
s/(\(([^()]*)\))/\1/  # remove a layer of any double parens
                      # not enclosing parens
ta                    # loop back to a if we changed anything
s/\(.\)(\([^()]*\))\(.\)/\1\t\2\n\3/
                      # convert (no-parens) to \tno-parens\n
                      # but only if they occur in the middle.
ta                    # loop back to a if we changed anything
s/^(\([^()]*\))$/\1/  # Remove global parens if they
                      # don't enclose any parens
y/<>/()/              # convert all our \t\n back to ()
```
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), ~~33 30 29~~ 27 [bytes](https://github.com/DennisMitchell/jelly/wiki/Code-page)
```
Ø(jẹⱮØ(Œ!ż€¥/ṢƑ€ÞṪ_Ø-fƊF’œP
```
A full program that accepts a string and prints the result.
**[Try it online!](https://tio.run/##y0rNyan8///wDI2sh7t2Ptq4Dsg6Oknx6J5HTWsOLdV/uHPRsYlA5uF5D3euij88QzftWJfbo4aZRycH/D/cDpSI/P8/WkkjMacgI1FTiUtHSQMIojWBAMRRSEotSdRIT8zNhUg6gJRpaOQk5ialJEKUaBRkZGpolGQAFWoWZELFijPTgTo08nNT0xPhIhoQIc1Ua4RNGkBTNJOgujSSoAIQPpCXqJKsqZGSmgYRqABKV2pqVEHlNdLy8zWSEouABiRWgQRAfLAAkA9UEQsA "Jelly – Try It Online")**
### How?
Partitions the input string at its redundant parentheses and prints the characters that remain.
The redundant parentheses are found by first finding the indices of all pairs of matching parentheses in a copy of the input string that has been wrapped in parentheses, and then filtering these to those which are bounded by any other (i.e. rule 1, but removing the inner pair).
For that, the list of indices of all pairs of matching parentheses are effectively found by pairing each closing index with the opening index that is closest to its left that has not been consumed by any previous closing index. This is actually achieved by constructing all lists of pairs of opening and closing indices and finding the one that (a) has no reversed pairs and (b) has the maximal list of opening indices when ordered by their respective closing indices.
```
Ø(jẹⱮØ(Œ!ż€¥/ṢƑ€ÞṪ_Ø-fƊF’œP - Link: list of characters, S
Ø(                          - "()"
  j                         - join with S -> parenthesised S
     Ø(                     - "()"
    Ɱ                       - map with:
   ẹ                        -   all indices -> [open_indices, close_indices]
            /               - reduce this pair of lists by:
           ¥                -   last two links as a dyad - f(open_indices, close_indices):
       Œ!                   -     all permutations of open_indices
         ż€                 -     zip each with close_indixes
                Þ           - sort by:
               €            -  for each pair:
              Ƒ             -    is invariant under?:
             Ṣ              -      sort
                 Ṫ          - tail -> X = list of open indices that pair ascending close indices
                      Ɗ     - last three links as a monad - f(X)
                   Ø-       -   [-1,1]
                  _         -   subtract [-1,1] from each index-pair
                     f      -   X filter keep those
                                    -> redundant parentheses index pairs
                       F    - flatten
                        ’   - decrement (from indices in parenthesised S to indices in S)
                         œP - partition S at those indices, discarding borders
                            - implicit, smashing print
```
[Answer]
# [Retina](https://github.com/m-ender/retina/wiki/The-Language), ~~53~~ ~~52~~ ~~51~~ 55 bytes
*-2 bytes by assuming balanced parentheses in the input*  
*+4 bytes to fix a bug*
```
.+
($&)
+`\((\((?>(\(()|[^)]|(?<-3>.))*)\))\)
$1
^.|.$
```
[Try it online!](https://tio.run/##VU7bToNAEH3fr9BKy5liMY1vtkLf/AUVisxSCiRFSOVBEf11nAVMdDLZ3XOZPXNOm@KV1/0cD3HvOgrWgpQTh4C075mTuiCifQd/u7r1XKIlhSStrLWK3M61VN@DT3XOpCAVkJS60GnDyLgshd4ZFThxqQ8sIuq8AJpcLFQXhngrMjGiKtOMR4gRU7qZ/oVMkjZm6AkJkCdbCeGQHgW9i/BBaI2CY1VB81mGuFUGDEgAKVnmr0qtMP8cwkyZkEhKhthECE2/yeMOmhKVmIV45MLZcNlyPj49X86urPkitLEk53rl3qi7zfbe83dBGO6jl/iz@/q2fwA "Retina – Try It Online")
**Step 1:**
```
.+
($&)
```
Surround the string with parentheses (in addition to whatever parentheses it may already have). An alternative way to do this for the same number of bytes would have been:
```
^
(
$
)
```
**Step 2:**
```
+`\((\((?>(\(()|[^)]|(?<-3>.))*)\))\)
$1
```
Remove redundant parentheses, assuming that all parentheses are balanced in the input string.
```
+                    # Loop substitution until its result is equal to the input
`                    # Delimiter between Retina flags and regex
\(                   # Opening parenthesis (redundant one)
(                    # $1 = the following:
    \(               # Opening parenthesis
    (?>              # Atomic group – once the following finishes matching, lock
                     # in the match and prevent backtracking into it.
        (
            \(       # Match an opening parenthesis
            ()       # Push a capture onto the \3 stack
        |       # or
            [^)]     # Match any character other than a closing parenthesis.
        |       # or
            (?<-3>.) # By process of elimination, this character must be a
                     # closing parenthesis, but only allow removing it if it
                     # corresponds to popping a capture from the \3 stack.
        )*           # Iterate the above as many times as possible, minimum zero.
    )
    \)               # Closing parenthesis
)
\)                   # Closing parenthesis (redundant one)
                     # (newline) - delimiter between regex and substitution
$1                   # Replace with $1
```
**Step 3:**
```
^.|.$
```
Remove the pair of parentheses that surround the entire string, which are guaranteed to still be there.
# [PowerShell](https://github.com/TryItOnline/TioSetup/wiki/Powershell), ~~112~~ ~~101~~ ~~95~~ 99 bytes
*-6 bytes thanks to VisualMelon*
```
[char[]]($p="($args)")|%{$p=$p-replace'\((\((?>(\(()|[^)]|(?<-3>.))*)\))\)','$1'};$p-replace'^.|.$'
```
[Try it online!](https://tio.run/##VY7rUoMwEIX/8xSIadmtBe34T0rbx1CBakjDZSZcBnCqXHz1upQ61kwmyXfO2eyWxVFWdSKVOrHI7U6eSHjlBQGw0jWA8Squ0cB@1hGz0qpkqbiQpg9Ae7sZT@y9PQY9bNfW48ZGXKCPtM2lyVbm4FxV7e3eZuaJtNZ9cKKiklwkwJSe5jpL8/KjwS6NgLXYHau0kVZS1M1A4ZWj/wm6lRe5PKo0l7rBYM4inSk0hhNwVSYcNaDlIS1ND2XDIeZZRvJudAEUz8IDJxPKJAVoEopgmY5CncYUhCKTMZ8QJkbpXP4FqsRwDEN4IQJ6ciYQDjIi@iTjC6EdHYiKAkJeURFvtRHORIAaDXPtYkvKvwQpl55ALVGc2woSQvztPM0QotDEOBCfNN84Xyadzy@vN8Ytm819ExZ4t7Tse@3JWbub7c7z/WD/9t71w7f5Aw "PowerShell – Try It Online")
Uses the same regex and algorithm as my Retina answer, so I figured it'd make sense to group into the same post.
Ungolfed:
```
$p = "($args)"
$c = [char[]]$p  # $c = $p converted to an array of characters
$c | ForEach {   # Iterate the following the same number of times as $c's length
    $p = $p -replace'\((\((?>(\(()|[^)]|(?<-3>.))*)\))\)', '$1'
}
$p -replace '^.|.$', ''
```
# [PowerShell](https://github.com/TryItOnline/TioSetup/wiki/Powershell), 94 bytes
Using [Neil's regex](https://codegolf.stackexchange.com/a/250642/17216), with a -1 byte golf from me:
```
[char[]]($p="$args")|%{$p=$p-replace'(^|\()\(((?>((\()|[^)]|(?<-4>.))*))\)(?=\)|$)','$1$2'};$p
```
[Try it online!](https://tio.run/##VY7dUoMwEIXveQrEtOzWUq3jlZTSx1CBakjDzww/GcCpAvXVcSl11Fwk@51zNruqPMqqTmSWDSxyusETCa@8IACmHIPxKq4N7GcdEVNWJVXGhTRh3/uAPgC4WwAqe2@PQQ/uxnrYrhAXiD6C6/jYMzSXJluze/NkMzXQ1Tp3dlRWkosEWKanhc7SQr032KURsBa7Y5U20krKujlReG3rv4JuFWUhj1laSN1gMGeRzjI0TgPwTCUcNVoJPKSj6aFsOMQ8z0nejS5AxvPwwMkElaQATUIRVOko1GlMQShzGfMJYWKU9uVfoE4MxzCEFyKgkjOBcJAR0QcZnwjt6EBUlhDyipp4q41wJgLUaJm/Lrak/EuQcpkJNBLFeawgIcSfydMOIQpNjAvxSfON82PS/fT8cmVcs9ncN2GBN0trdas92htn6@483w/2r29df/oyvwE "PowerShell – Try It Online")
[Answer]
# Python3, 275 bytes:
```
lambda x:P(f(x)[1])
P=lambda x,l=0:[]==x*0and'('*l+''.join(P(i,1)for i in x)+l*')'or x
def f(s):
 r,*t=0,''
 while s:
  h,*R=s
  if'('==h:s,T=f(R);t+=T,''
  elif')'==h:r=1;break
  else:t[-1]+=h;s=R
 return s[r:],(lambda x:len(x)<2and[]==x[0]*0and x[0]or x)([*filter(None,t)])
```
[Try it online!](https://tio.run/##XZLLbtswEEXX9VcQQgDNyGxgt5tCDoF8QWAY2alakBFlsaEeIBlUadFvd0cPK1a44tw5uDOXYPceqrb5/qNzl0L8vFhZq0KyPj1CCT1m@xw3R3FVuRW7NMuF6JOdbIoY4sRu4/j@V2saOILheyxbxwwzDetxa5MYY6r7TaFLVoLHdMMcT4LY8TjesN@VsZp5ElnFk5PwdDEluQpRpZ4/ixJOeAhb8TziTFvq4th1Yn9QTsvXUfY6DdnXfb4V1cGLEw3R4c01zGcuzTksoaxuKNTDN9p9TJHt8jEIG27DoghZUhobtIOnttE8YI6XIZLkTA2p/tI8FoG0XSUxStnqRKMc8YmhkyGdNRVlc58pHSScZV2vjNb6hD4OtjDH@DCc9Vm@ju0qAxAqMsHOLHC0lq@wN2caA22tzzdbfNY/YJhU1IcFj9byTXqgXVGtHoBeDtWCqJlZIwqWkQTIuxcE@j63zCd9Ynsye0f4M7kN5VRR@9@9Cbr2QN/vi/Reu8AKGiKEuvwH)
[Answer]
# Regex (.NET), 200 bytes
`s/(?<=()^\(*|)(?=\(((\()|[^)]|(?<-3>.))*(?=\)+$()|)(\).*)).(?=\1\4|\(((\()|[^)]|(?<-7>.))*\)\5$)|\)(?=\)*$()|)(?<=(.*\()(?<=^\(+()|)((\))|[^(]|(?<-12>.))*.)(?<=\8\10|^\9\(((\))|[^(]|(?<-14>.))*\).)//g`
[Try it on regex101!](https://regex101.com/r/xRyPIS/1)
This is a single regex substitution, to be applied once. It's one and done, erasing all the required parentheses in a single pass. This is the only kind of substitution that can be easily used on regex101; ones requiring multiple passes need to have their output manually fed back as input each time.
```
    (?<=
        ()             # \1 = set if to the left is a continuous stack of
                       #      opening parentheses
        ^\(*
    |
    )
    (?=
        \(             # Match an opening parenthesis.
        (
            (\()       # Push an opening parenthesis onto the \3 stack.
        |         # or
            [^)]       # Match any character other than a closing parenthesis.
        |         # or
            (?<-3>.)   # Match what must, by process of elimination (with the
                       # assumption that the input is balanced) be a closing
                       # parenthesis, while popping a capture off the \3 stack.
        )*
        (?=
            \)+$
            ()         # \4 = set if to the right is a continuous stack of
                       #      closing parentheses
        |
        )
        (\).*)         # \5 = mark spot by capturing everything to the right
    )
    .
    (?=
        # Assert \1 and \4 are both set
        \1\4
    |          # or
        # Assert that the next deeper set of parentheses is adjacent on both
        # sides to the pair we just processed.
        
        # First, match balanced parentheses the same way we did above, but one
        # level deeper, starting by asserting that the next opening parenthesis
        # is immediately adjacent.
        \(
        (
            (\()
        |
            [^)]
        |
            (?<-7>.)
        )*
        \)
        
        # Assert that this brings us exactly to the spot we marked with \5.
        \5$
    )
|
    # Do the same thing as above, but exactly in reverse. Note that lookbehinds
    # are tokenwise evaluated from right to left (bottom to top in this view
    # here). That means alternatives are still evaluated left to right within a
    # group, but the matching of groups/characters/backreferences is done from
    # right to left.
    \)
    (?=
        \)*$
        ()             # \8
    |
    )
    (?<=
        (.*\()         # \9
        (?<=
            ^\(+
            ()         # \10
        |
        )
        (
            (\))       # \12
        |
            [^(]
        |
            (?<-12>.)
        )*
        .
    )
    (?<=
        \8\10
    |
        ^\9
        \(
        (
            (\))       # \14
        |
            [^(]
        |
            (?<-14>.)
        )*
        \)
        .
    )
```
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), ~~31~~ 29 bytes
```
Ø(j
ẆṚẹⱮØ(>/SƊÐḟÇ€¹œṣÇ}jʋƒÇḊṖ
```
[Try it online!](https://tio.run/##y0rNyan8///wDI0sroe72h7unPVw185HG9cBBez0g491HZ7wcMf8w@2PmtYc2nl08sOdiw@312ad6j426XD7wx1dD3dO@///v5IGCCQmaWoma2pqpKRqVgC5lZoaVZpAoAQA "Jelly – Try It Online")
If Jelly had a builtin for "is a balanced string", this would have been much shorter. A "replace substring" builtin could also have helped, as would better chaining rules for dyads. (My recommendation for dyad chains is to start with the left argument, then mostly use the monad chaining rules with the right argument, keeping the special case for `+×` but discarding the other dyad chaining rules. That would have saved two bytes in this program, compared to Jelly's current chaining rules, and would also be much more intuitive.) Larger-scale problems like this one can be good at exposing some of Jelly's shortcomings.
Thanks to @Unrelated String for pointing out that I'd missed a builtin – using `ẹ` rather than implementing it manually saved two bytes.
## Explanation
### Helper function `1Ŀ`
The helper function encloses a string in parentheses, and is called using the shortcut `Ç` (which calls the other function, when only one function is available, thus there's no need to state its name explicitly).
```
Ø(j
Ø(     ["(", ")"]
  j    join this list by {the function argument}
```
### Main program
Outputs a string with redundant parentheses removed.
```
ẆṚẹⱮØ(>/SƊÐḟÇ€¹œṣÇ}jʋƒÇḊṖ
  ẹⱮØ(>/S                      check if a string is unbalanced
   Ɱ                           for each of
    Ø(                           ["(", ")"]
  ẹ                            find the indexes at which it occurs
       /                       compare these two lists with each other
      >                          by greater than of corresponding elements
        S                      sum (i.e. true if there are any nonzeros)
         Ɗ                     parse the entire check as a single group
Ẇ                              find each substring of {the input}
 Ṛ                               ordered from longest to shortest
          Ðḟ                   keep only those that are not
  ẹⱮØ(>/SƊ                       unbalanced
            Ç€                 call `1Ŀ` on each of them
                     ƒ         iterating over this list, using an accumulator
                      Ç          initialised to `1Ŀ` of {the input}
                    ʋ            do all of the following, parsed as a group:
               œṣ                  split {the accumulator} on
                 Ç                   `1Ŀ`
                  }                    of the current list element
                   j               and join on {the current list element}
                                 {to produce the new accumulator}
              ¹                (fix for parser ambiguity)
                       ḊṖ      delete the first and last elements
```
There's an interesting subtlety in the check for unbalanced strings. The main idea is to check the indexes of the opening parentheses and of the close parentheses, and try to find an *n* for which the *n*th opening parenthesis appears after the *n*th closing parenthesis (thus, we're comparing the two lists with an element-wise `>` that compares corresponding elements of the two lists). However, if the number of opening parentheses differs from the number of closing parentheses, the element-wise `>` will be trying to compare two lists of different lengths. In this case, it ends up just outputting the extra elements literally (so, e.g., comparing [4,3,2]>[1,2,3,4] outputs [true, true, false, 4]). This doesn't make much sense mathematically, but means that (because Jelly uses 1-indexing) the result will contain a truthy element if the two lists are different lengths, so the string will be seen as unbalanced (which is correct – if the number of opening and closing parentheses differ, it can't be balanced). It took me a while to find a check for string balance that could handle both the equal-parenthesis-count and unequal-parenthesis-count cases without needing a separate check, but I'm happy with this one.
Once we have a list of all the balanced strings (say *`x`*), the algorithm is to replace `((*x*))` with `(*x*)`. As long as this is done in order from longest to shortest, all the internal redundant parentheses will be removed.
To remove redundant parentheses around the string as a whole, the idea is to enclose the string in parentheses to start with, and remove them at the end, so that the same redundant parenthesis removal algorithm works to remove both internal and external redundant parentheses. This fits in rather nicely with the `ƒÇ` construction, allowing the loop to start with a parenthesis-enclosed input without requiring any extra plumbing (if attempting to use the input directly rather than a function of it, it might well require spending another byte on parser ambiguity fixes).
[Answer]
# [05AB1E](https://github.com/Adriandmen/05AB1E/wiki/Commands), ~~50~~ ~~47~~ 46 [bytes](https://github.com/Adriandmen/05AB1E/wiki/Codepage)
```
®1‚„()ISkkƶ0KvyDdi‚ˆ]¯.Æʒ`αP}€¨¯ʒ®Ig‚Q}«˜Ä<õsǝ
```
[Try it online](https://tio.run/##AWEAnv9vc2FiaWX//8KuMeKAmuKAnigpSVNra8a2MEt2eURkaeKAmsuGXcKvLsOGypJgzrFQfeKCrMKowq/KksKuSWfigJpRfcKry5zDhDzDtXPHnf//KGFiYygoMTIzKSkoaykp) or [verify all test cases](https://tio.run/##TZDBSsNAFEV/JQwu3oNSrC4V6sKdG0WUQin4JkmT0IYUW4opFIpI15KlqGtBaSmoHzABhRb8iPxIfDMTi2HI3HPvnTcwyZBk5JfjtCmcYp45opnm9xeiVItGMXsoZs@ArfNe7@tz92ScHnsRm5t5Ry3r@XydXf2sTqfF7Zt6Uct1phatgOOzqXrdPOZ3h/nH8PupFPXLmnov2wJIugCNvX1E6CGKmiBZMWudam1kfxCSUfy1EU3Bkf6IIKA4NtGR7gD0KZYe2QmDMAIYhVzDQWStYRRwHZLYD@jPAOugf7C9A3gCSnsEZMUGGWjHRfD8ruEbDlOEiU2hmyQg6ZoP04RZo2HGf7OBR6Nbjec3YN7e4IiG/oGRxIlrEIy0BQbihXpHJKpchCrVir3OLw).
**Original (completely different) 50 [bytes](https://github.com/Adriandmen/05AB1E/wiki/Codepage) approach:**
```
ŒʒÁÁ4ôć„()©º{RQiJ®Ã®õ:õQ]vyD¦¨:}Ð'(Å?i¦¨‚¤®Ã®õ:õQè
```
[Try it online](https://tio.run/##yy9OTMpM/f//6KRTkw43Hm40ObzlSPujhnkamodWHtpVHRSY6XVo3eFmIN5qdXhrYGxZpcuhZYdWWNUenqCucbjVPhPEe9Qw69ASZGWHV/z/r5GYlKyhYWhkrKmpka2pCQA) or [verify all test cases](https://tio.run/##TZCxS8NAFMb/lXAIvgdFqDrVIQ5ObhV0KR3epWkSbEiwpZiK0CLqWjI6OInUxaldHO908c/IPxLf3cXScEm@3/d99w4uG5NMwnpa@MKrnkpP@IVeXor6p/wt9UIvjvX6@7mavwKqD/V1d9FNztWnfuB309Gbbn9anKl3terc6@U@6Ec/MVTNX9Tbbk2vanFw1VLruieAZADQPjxChGtE0RIkG2ZtUqOtHOUxWcVPD9EWPBlOCCJKUxudmg7AiFI5IDchjxOAScw1zBNnjZOI65ClYUT/BjgHw5PtGcATULotIBu2yEB7AcIgHFq@5bBAmLkUhlkGkm54M82YDVpm3JkNPBqDZjzfAfP2BE@0zQesJE4Ci2ClKzAQLzR/RKLGRWhSo9jr/wE).
Both can most likely be golfed quite a bit more, but these kind of challenges aren't really 05AB1E's (nor my..) strong suit, especially with all these edge-cases.
**Explanation 1:**
Step 1: Get a list of all index-pairs for the balanced parenthesis:
```
®1,           # Push pair [-1,1]
   „()        # Push string "()"
      I       # Push the input-string
       Sk     # Get the index 0-based index of each character in string "()", 
              # or -1 if it's neither
         k    # Use that to index into the [-1,1] (basically replacing -1s with 0s and
              # vice-versa, without changing the 1s)
          ƶ   # Multiply each value by its 1-based index
           0K # Remove all 0s
v             # Loop over each index:
 y            #  Push the current index to the stack
  Ddi         #  Duplicate it, and if it's a positive index:
     ‚        #   Pair it with the top negative index of the stack
      ˆ       #   Pop and add this pair to the global array
]             # Close the if-statement and loop
 ¯            # Push the global array
```
[Try just step 1 online.](https://tio.run/##TZC9SsRAFIVfJQwW58IiRkuLtbCzFEVYtriTzCZDNiS4y2K22mprsbTwEbQRHyCCjW@RF4l3fnYxDJnznXPmDkyzYm3NuOmmKhn2L4madt/Pd2rs39Nh9zrs3kAPt1X183V2s@mucyvm737ef4zq9H7Sf44zBdYZkJ5fEKEiUhPFOrJolzrt5bIt2Sv5ZkS@kGizZhRc1z66ch1gybXOOUxoSwusS6lRa4O1soXU0dSm4IOB4JC5PN4BmUA6HIGO7FGATzJCbhaenyTsCNuQYtE00Pwoh3kr7NCz4L/ZkNGUxfHyBsLHGxKVuh@8ZEkyj/AyFARYFrmdiDm6hJg6Jd78Dw)
Step 2a: Keep all the pairs which has a neighboring pair available:
```
.Æ             # Get all unique pairs of this list of index-pairs
  ʒ            # Filter it by:
   `           #  Pop and push both pairs separated to the stack
    α          #  Get the absolute difference at the same positions
     P         #  Product to check if both differences are 1
  }€           # After the filter: map over each pair of pairs:
    ¨          #  Remove the last one
```
[Try steps 1 and 2a online.](https://tio.run/##TZDBSsNAFEV/JQwu7oNSjC5d1IU7N4IoQin4JkmbkIYEW4opCMVF1tKlC9cuRBHED0jBhQU/Ij8S38zEYhgy99x75w1MPmOdRO2iHCivqdaeGpSbhwvV1q9@s3psVk@gq/M0/frcP12UJ2Ei5rYa1W/9TfW9vv55P7tr7l/q51b1L3v1RztUYB0A/sEhEVIi1VOsOxZtUqOtnBYxWyXfkMgWPB3NGRPOMhsdmw4w5UyH7CYUcQLMY6lRkThrlkykjjyLJvxnwDkUHe3ugEwg7Y5Ad2xRgPcCQhiNLd9KWBKWLsU4z6H5Rg7zUtigZcF/syGjKejGyxsI727wlG9@sJIlCSzCSlcQYFlkdiLmziV0qVHijX4B)
Step 2b: If the input is surrounded by a pair of parenthesis, add its index-pair as well:
```
¯              # Push the global array of pairs again
 ʒ             # Filter it by:
     ‚         #  Pair
  ®            #  -1
   Ig          #  with the length of the input-string
      Q        #  And check if the current pair is equal to this
 }«            # After the filter: merge the two lists of step 2a and 2b together
```
[Try steps 1 and 2 online.](https://tio.run/##TZDBSsNAFEV/JQwu3oNSGl26qAt3biyiFErBN0mahDQk2FJMoVBcZC1ZunAtKC2C@AFTcFHBj8iPxDczsRiGzD333nkDk81IxkGzKPrCqcvKEf1i93gtGrVx6/VTvX4GHF4lyddn72JRnPsxm9/lWG27u3Jf3f68X67qhzf1orb7Sm2GIceDlXptRPemoz6akQCSHoB7fIIICaLoCJIts9ap1kZO84iM4m@EaAqODOYEIaWpic50B2BKqfTJTsijGGAecQ3z2FqzOOQ6ZGkQ0p8B1sHg9HAH8ASU9gjIlg0y0JGH4AcTw/ccFghLm8Iky0DSHR@mJbNGw4z/ZgOPRq8dz2/AfLjBEa7@gZHEiWcQjLQFBuKFekckal2ENtWKvfEv)
Step 3: Remove all characters at those indices, and output the result:
```
˜              # Flatten the list of index-pairs
 Ä             # Get their absolute values, to make the negative indices positive
  <            # Subtract 1, to make the 1-based indices 0-based
   õ           # Push an empty string ""
    s          # Swap so the list of indices as at the top
     ǝ         # Insert the empty string at those indices in the (implicit)
               # input-string, basically removing them
               # (after which the result is output implicitly)
```
**Explanation 2:**
Step 1: Get all substrings of the input, and filter it to only keep those with double leading/trailing parenthesis-pairs:
```
Œ                  # Get all substrings of the (implicit) input-string
 ʒ                 # Filter it by:
  ÁÁ               #  Rotate the substring twice to the right
    4ô             #  Split the string into parts of size 4
      ć            #  Extract the first item
       „()         #  Push string "()"
          ©        #  Store it in variable `®` (without popping)
           º       #  Mirror it to "()()"
            {      #  Sort it to "(())"
             R     #  Reverse it to "))(("
              Qi   #  If the extracted head is equal to this:
  J                #   Join the remainder-list together
   ®Ã              #   Only keep all "()"
     ®õ:õQ         #   Check if these parenthesis are well-balanced:
     ®õ:           #    Keep replacing all "()" with "" until it is no longer possible
        õQ         #    Check if the result is ""
 ]                 # Close both the if-statement and filter
```
[Try just step 1 online.](https://tio.run/##TZCxTsMwFEV/JbIY7pMqpAITDGVmayVYqg7PaZpYNErUVoiUhQoJVpQPYGdhahfGGBY@oz8Snu1QETnJPfc@X0sulqxN0t5VAxXtn@tIDSr7eq3a7/qnthu7ObPbr5f94xuoeW8@H0ZDc9V82Cd5d@d2N5y06vim12zbsQLrGOifnBLhlkj1FOuORbvUaS/nZcZeyTMm8gORTlaMlPPcR5duBphzrqccGsrMAKtMxqg0wVqaVMZR5EnKfwaCQ8nF4QxIA@mwBbpjjwJ8FBOmyczzvYQVYR1SzIoCmheymdfCDj0L/uuGVFPc1csdCB9OiFTffeAlSxJ7hJdhQIBlkfsTMXcuoUudEm/yCw)
Step 2: Replace all these filtered substrings with it's leading/trailing parenthesis removed in the input-string:
```
v                  # Loop over each of these filtered substrings:
 y                 #  Push the current substring
  D                #  Duplicate it
   ¦¨              #  Remove the first and last characters (the "(" and ")")
     :             #  Replace it in the (implicit) input-string
}                  # Close the loop
```
[Try steps 1 and 2 online.](https://tio.run/##TZAxS8NAGIb/Sjgc3g@KUHWqQx2c3CroUjp8l6ZJsCHBlmIqgkXQVfIDHEUXp3ZxvNPFn9E/Er@7xGK4JO/zvt@9B5fPWKdRvSj7Ktg@VoHql/b5QtXf1U9lV3Z1ZNdfT9v7F5B5N5@354P0zHzYB3k3PbsZjBblqXk1b727Wu1fdsy6HiqwDoHuwSERrohUR7FuWbRLnfZyWiTslTxDIj8Q6GjOiDnLfHTiZoApZ3rMTUORpMA8kTEq0saapbGMI8@imP8MNA5Fx7szIA2kmy3QLXsU4L2QMI4mnm8kLAnLJsUkz6H5WjbzUtihZ8F/3ZBqCtt6uQPh3QmB6roPvGRJQo/wshkQYFnk/kTMrUtoU6fEG/0C)
Step 3: Remove a potential leading/trailing parenthesis-pair, if everything else in the string is still well-balanced without it, and output the result:
```
Ð                  # Triplicate the current string
 '(Å?i            '# Pop one copy, and if it starts with a "(":
      ¦¨           #  Remove the first and last character from another copy
        ‚          #  Pair it with the string
         ¤         #  Push the string with first/last characters removed (without
                   #  popping the pair)
          ®Ã®õ:õQ  #  Same as in step 1 to check if its parenthesis are well-balanced,
                   #  resulting in 1 or 0 for truthy/falsey respectively
                 è #  Use that to index into the pair
                   # (after which the result is output implicitly)
```
[Answer]
# [Python 3.8](https://docs.python.org/3.8/), ~~215~~ ~~204~~ ~~195~~ ~~214~~ 205 bytes
*-11 bytes [thanks to Unrelated String](https://chat.stackexchange.com/transcript/message/61726656#61726656)*  
*-9 bytes thanks to att*  
*~~+19~~ +10 bytes to properly handle input containing single quotes and/or backslashes*
```
lambda x:P(Q(eval(repr('"\''+x)[3:].translate({40:"',['",41:"'],'"}))))[1:-1]
P=lambda x:[]==x*0and'(%s)'%''.join(map(P,x))or x
Q=lambda x:(x:=[Q(y)if[]==y*0else y for y in x if y])==[[*x[0]]]and Q(*x)or x
```
[Try it online!](https://tio.run/##bZTNjpswEMfPy1OMrEYepzRKtHtYEVmq1FNvyZlwMMQkrPgS0Aqy2msfoI/YF0nHfAWS9cn85s9/ZoyHvKnOWfr8mhfXUB6usUr8o4La2eEe9W8VY6HzAjk7cP61Fu6z462qQqVlrCqN7y9rh3Hb5cx@2dDOszn7ELTcjfNt41k7Ofq5npT1cq3SI8dFKfiC89VbFqWYqBx3di1EVkBt7W9vYO1Id4@NiELzcrNc67jU0EBIygaiFGqIQmg8IaXrLmt37Xke@cMel3Xrdq10WZUg4d0CAIYqzs9KMAdmi7WY2Z2Glms6mKuY28fB15XCk0qSmdGcd9Lvxhax6@dm2PMeD2nzc4RYnclE5NEoZnM8iMvoRGkwS/RpUsU9v4mxo0JvRzmb40n3SLUKf3YAdHLCHyV@r5lLfBxTkkB9CQQedTjV3PFOW5NZI/Byd@KGd3hIG2YZ@qqg0tRl4jnnndaglhGa5p/zWcdIDYtg8pEGGIy6gIS@uOucBQO8@SlzfMHd/THYH90Cc4jq8ZYZPvU6MPFwXYEd2Cjgn8SB8ZvB4RMFI0qKD@si11uIs/REY2K2br8HR0KiauwfbYh12v0HTM3t/CnbNxPYDtgqqnRSovCsLgKPIcd6olG9OJAXUVqhsJ4ucrOFgGYzJNNtz/mPrCh0UFHB3Mx2IKUP7dTzn2nQx7gNOj1Kvijh35@/sCg5LGAobxW//SqroXJhQ8vpy4rrfw "Python 3.8 (pre-release) – Try It Online")
This was inspired by [Ajax1234's **275 byte** answer](https://codegolf.stackexchange.com/a/250615/17216), which had already had 48 bytes knocked off it (starting at 323 bytes) by okie, Unrelated String, and Steffan.
The `P()` lambda here is based directly on its `P()`. The rest is completely refactored to be functional rather than imperative, but follows a similar approach:
1. Convert the string into a ragged list.
2. Do the parentheses removal on the ragged list.
3. Convert it back into a string. This will be surrounded by outermost parentheses whether or not the original was.
4. Remove the outermost pair of parentheses.
In more detail:
`repr('"\''+x)[3:]` - backslash-escape single quotes and backslashes, and surround with single quotes (forces `repr` to use single quotes even if they're in the input, by putting a double quote in it)
`.translate({40:"',['",41:"'],'"})` - replace `(` with `',['` and `)` with `'],'`
At this point, e.g. `"(sig(ma)(om)ega)"` will have become `"'',['sig',['ma'],'',['om'],'ega'],''"`. When we then `eval()` it as Python code, it becomes a ragged list that is a `tuple` on the topmost level, containing lots of empty strings, `''`, mixed in with the strings/lists we want.
The job of `Q()` is to recursively remove the empty strings (which results in the original string converted to a ragged list), and unnest lists whose only element is another list.
`[]==y*0` is true iff `y` is a list – golfed down from `type(y)==list`.
`x:=[Q(y)if[]==y*0else y for y in x if y]` recursively applies `Q()` to `x`'s list elements, and removes its empty string elements.
`x==[[*x[0]]]` is true iff `x` is a list whose only element is another list – golfed down from `type(x)==list and len(x)<2`.
`Q(*x)` recursively applies `Q()` to the result of dropping one pair of surrounding brackets from `x` (unnesting it by one level).
`'(((((a)(b)))c))'` will have become `[[['a'], ['b']], 'c']`.  
`' beta(gamma)'` will have become `[' beta', ['gamma']]`.  
`'(alpha)'` will have become `['alpha']`.  
`'epsilon'` will have become `['e', 'p', 's', 'i', 'l', 'o', 'n']`.
`P()` then converts the resulting ragged list back into a string, surrounding lists' contents with parentheses and joining the strings. `[1:-1]` removes the outermost pair of parentheses, which will be there even if the original input didn't have them.
[Answer]
# [Retina 0.8.2](https://github.com/m-ender/retina/wiki/The-Language/a950ad7d925ec9316e3e2fb2cf5d49fd15d23e3d), ~~53~~ 52 bytes
```
+`(^|\()\(((?>((\()|[^)]|(?<-4>.))*))\)(\)|$)
$1$2$5
```
[Try it online!](https://tio.run/##VY7NUoNAEITv@xQaN9KTmFhaejERcvMVVDbILCFAVRAqclBEXx2HH6t0D7vzdffszDGusldup3gI23mIoDEgA8BzASkbP6BtA2@9uHGXRDMiQzDUaFL6Sl/r27YFH8qUSUkTfJKjTmxcMRLOc5E3nQscOLc7FhNlmgFVKhEqs054yxIJosjjhAfEwBSvxn8hnWS7MOxIAlKyjgi7eC/0LsYHoe4c7IsClo/SxLXqoCcBUrLMX5dqUf4lRBlnQkZS1I@NRLD0O3nYwVKkom4hHjQz6R9H7sen59PJmZ6eGwczml8slpfqbrW@d72Nb8w2eAk/m69v5wc "Retina 0.8.2 – Try It Online") Link includes test cases. Explanation: Works by removing all inner sets of balanced parentheses that are contained by parentheses or match the whole string. Uses the assumption that all parentheses are balanced meaning that the input can't be something like `(@))((@)`. Edit: Saved 1 byte thanks to @Deadcode pointing out that I didn't need to use lookarounds.
[Answer]
# [Python 3.10](https://www.python.org),~~194~~ ~~193~~ ~~183~~ ~~177~~ ~~174~~ ~~173~~ ~~165~~ 163 bytes
* -1 byte since one string didn't need to be raw
* -10 byes by using a recursive function to re-assemble the output rather than regex
* -6 bytes thanks to @deadcode by using `map()` instead of a comprehension
* -3 bytes by copying string comparison logic from dead code's answer
* -8 bytes thanks to @deadcode and @steffan
* -2 bytes by using old-style string formatting instead of modern f-strings
Since it's a slightly different approach than the other python answers I think this deserved it's own answer. Probably further golfable.
```
lambda x:u(eval(s('\)\(','),(',s('(?<![^(]),','',s('[^()]+',lambda i:",%r,"%i[0],x)))))[1:-1]
u=lambda g:g if''==g*0else"(%s)"%"".join(map(u,g))
import re;s=re.sub
```
[Attempt This Online!](https://ato.pxeger.com/run?1=bZNNcpswGEAX3XEKRQ3Vp0R2k10HhybHaAskFURgtcYwCDp20vQSXXbjmUxzp_Q0FRZgxFgLQE-P70egPy_ltl4W691z6od_mzqdfXh9XvE8vudo4zUgfvAVKCAhDYEwQpm-6ilcX50EtxBRpuGe6BmNzgnr3pUeZm7FsCuDi4htaDuCS292GTmN3zmZlyGZEuL72dmFWCmBwVUUuxjPvxVyDTkvoWEZpY7My6KqUSUWyq_EXDWxKfXfm9-1UPVdwpVQyEcIPWLgq3LJKfaQNfAeY-a0z6BH0JZkWzjo1lEsag4Zz3MrkM2NetOGBTAtHQJ2vMN92nIpAeqlDkJLOcjYxr2sZKbTQJGLbFTFlB9kMJSKxaBjG4-6B10rja0N0DtH40GJO8dWYhhSaoGfJhTuRTp2Jty4Gx1sS-FhsuMtN7hPmxYFxLzSpfGHUUybG7dFe6bROL_NrY5BN0yT0UfqYTJ4iRZjOukcJz0cvBBPf56Wh3gQyJF1hMmwrjeHIYBs8gnSlmZ9nk-fv5yE-O2p-y4kcEbP2Wz-XuvHuXnFW1z5H69vgjCMbu--Pv58-kUw8o5z58lx0qJC38WW6XPeCCTX6HCg5rIWuQLqtYFRWcl1DSlomRqbmYnvm5kSpU_CmlBzNnc7c_8P)
## Explanation
1. Extract all sequences that don't contain parenthesis and surround them by quotes and commas.
```
a = re.sub('[^()]+',lambda i:",%r,"%i[0],x)
```
2. Remove commas at the start of the string and right after opening parenthesis
```
b = re.sub('(?<![^(]),','',a)
```
3. Eval the string then repr it again, this part actualy does the parenthesis removing
```
c = eval(b)
```
4. Basically a custom repr function that ignores strings
```
u=lambda g:g if g*0==''else"(%s)"%"".join(map(u,g))
d=u(c)
```
5. Remove the first and last character
```
f=e[1:-1]
```
[Answer]
# [JavaScript (Node.js)](https://nodejs.org), 112 bytes
```
a=>a.map((c,i)=>c=='('?a[a.push(!i|a[i-1]==c?[c]:c)-1]:c==')'&&(a[p=a.pop(),i+1]||c)==c?(p[0]='')+p&&c:c).join``
```
[Try it online!](https://tio.run/##XZHBcoIwEIbvPkXKdGB3xEx7lYn20nMfIMOMSwwQC5IR2qmtPjsNgijuLV@@/JtsdvRNtToY2yz21Va3qWhJrIiXZAFUaFCslBABBGuSxO1XncOTOZE0i9dYCLWWKl4qdItlp2Hg@0DSCqdWFjA089f4dFLYqWDlSyyCAOfW95U7xXeV2W82baPrRlGtaybY34wx5gEVNif0lmxS3gV7Ye@4kuhqanly2GeJbggyKstJ0JT36lsXC1BQmWzpFjjwAV/b2twANLkLQWtG2Zviq1ybzLWBqtTZ3S0e@U2GnqKORt2b4rvXg7srJpMBuMlhMirJ4EyVBMaWTqBnhbDV6b3zwHv3x4UdEX4fJt7xHjvvHM0@kp1WDf/UxxrGj0WeVod3UjkYJlaXT1bVvq4KzYsqgxQk59zEyIRg4yFp4pCNWxjNzhi1/w "JavaScript (Node.js) – Try It Online")
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), ~~32~~ ~~30~~ ~~28~~ 27 bytes
```
=þØ(_/µJṁ"Äo¹ṁɗ\ḟ"JċⱮ`HnAɓx
```
[Try it online!](https://tio.run/##y0rNyan8/9/28L7DMzTi9Q9t9Xq4s1HpcEv@oZ1AxsnpMQ93zFfyOtL9aOO6BI88x5OTK/4fbn/UtCby//9oJY3EnIKMRE0lLh0lDSCI1gQCEEchKbUkUSM9MTcXIukAUqahkZOYm5SSCFGiUZCRqaFRkgFUqFmQCRUrzkwH6tDIz01NT4SLaECENFOtETZpAE3RTILq0kiCCkD4QF6iSrKmRkpqGkSgAihdqalRBZXXSMvP10hKLAIakFgFEgDxwQJAPrINGkALNJNhliQDxZI04fbEAgA "Jelly – Try It Online")
Removes every pair of parentheses which is the only thing contained by either an outer pair or the entire string.
```
=þØ(                           Table equality with "()"
    _/                         and subtract the close parens from the open parens.
          Ä                    Cumulative sum, to get depths,
      µJṁ"                     repeat each index by its depth,
               \               and scan by:
           o                   overlay the element on the accumulator
            ¹ ɗ                then
             ṁ                 trim the accumulator to the element's length.
                ḟ"J            For each result, filter out its index.
=þØ(_/µJṁ"Äo¹ṁɗ\ḟ"J            This results in a list of lists where each list
                               uniquely represents the deepest pair of parentheses 
                               containing the corresponding element.
                   ċⱮ`         Count how many times each list appears.
                      H        Is each count halved
                       n       not equal to
=þØ(_/µ                 A      if the corresponding element is a paren?
                         ɓx    Replicate the input by that result.
```
[Answer]
# [Python 3.8](https://docs.python.org/3.8/), 190 Bytes
```
def f(s,r="",o="("):
 while o in s:a,b=s.split(o,1);i=[b.count(o,0,i+1)-b.count(")",0,i+1) for i in range(len(b))].index(-1);s=b[i+1:];r=r+a+o+f(b[:i])+")" if r+a+s else f(b[:i])
 return r+s
```
No regex library!
This works by going through each open paren, finding the matching close paren, and recursing on the contents. If, at any recursion level, the parens identified enclose the whole string being considered, they are redundant and discarded (as they either enclose the whole string or enclose a substring that is immediately surrounded by another pair of parens).
Un-golfed Version:
```
def f(unparsed_string):
    ret = ""
    while "(" in unparsed_string:
        # Split on first open paren
        first, rest = unparsed_string.split("(", 1)
        # Find index of matching close paren; it's the first index in `rest`
        # where the number of close parens is one more than the
        # number of open parens.
        # This will error if the parens are unmatched
        close_paren_index = [
            rest[:i+1].count("(") - rest[:i+1].count(")")
            for i in range(len(rest))
        ].index(-1)
        unparsed_string = rest[close_paren_index+1:]
        if (ret + first) or unparsed_string:
            # Recurse on the contents of the identified parens
            ret += first + "(" + f(rest[:close_paren_index]) + ")"
        else:
            # If the identified parens are the beginning and end of
            # the string, they are redundant; return just the recursed payload
            ret = f(rest[:close_paren_index])
    return ret + unparsed_string
```
[Answer]
# [JavaScript (Node.js)](https://nodejs.org), 206 bytes
```
s=>eval(`[${s[[].__proto__.toString=function(){return this[1]?this.map(e=>Array.isArray(e)?`(${e})`:e).join``:this[0]+''},r='replace'](/[^()]+/g,m=>JSON.stringify(m)+',')[r](/\(/g,'[')[r](/\)/g,'],')}]`)+''
```
[Try it online!](https://tio.run/##XZLBbqMwEIbv@xQWisSMQmn3mop099pDe@jRZYNxDHEKGBk3ahrl2bODA0moL8jf/3nGY7EVO9FJq1t315i1OhXJqUuWaicqyPjs0HGexqtVa40zq1XszJuzuimT4rORTpsG8GCV@7QNcxvd8d/pU/@Na9GCSpZ/rRX7WHf@CwqfMpgd1BGzhcJ4a3STZQt/7iGdh@ExskloVVsJqcIU7vk/wHR@X0Z1snx@e32JO99bF3uocR5GIXJL2juQEvJxh/0upfCYZmSFp8JYBtI0nWP8I2K7lJmCveZbJV2sGiqpOjj8YiwAUbUbgcGCXVbgURD5mBZHWlch4OeI5coJKEVdX45Pmbf@9LUAKlHna3EuM7ABDX3ajQZwGzqOrfZeMEWD1@mSaoOpVTm0/ckuHpwhqkdvBlN0nQ/oXphfRqQnwXxM8yG@pjmMPSgTM4mwVsUY/2Be@6ISe4Tvm0fs2RkNfQpjIBeWriG@h0pT5rWeeERkbDhltzMBjYRyePIRyFGR5OR4M1sgR0DKEZH1f4@pVFyZEgr4QJYkCdtFjH6nfouPp/8 "JavaScript (Node.js) – Try It Online")
[Answer]
# [Rust](https://www.rust-lang.org), ~~408~~ 396 bytes
I made this problem way to hard for myself
```
let f=|b:&[u8]|->Vec<u8>{let v=(0..).zip(b).scan(vec![],|a,(b,c)|Some(match c{40=>{a.push(b);None}41=>{Some((a.pop()?,b))}_=>None})).flatten().collect::<Vec<_>>();for k in &v{if k.0==0&&k.1==b.len()-1{return b[1..b.len()-1].to_vec();}for l in &v{if l.0==k.0+1&&l.1==k.1-1{return [&b[..k.0],&b[l.0..k.1],&b[k.1+1..]].concat();}}}return b.to_vec();};let g=|mut b:Vec<u8>|{while f(&b)!=b{b=f(&b)}b};
```
[Attempt This Online!](https://ato.pxeger.com/run?1=TVFLjpwwEFW2fQo3ilBZQ1uNNIsWDExOkE2kbBBq2bT5qM1HYHoyDdwgN8hmFskqBxolp0kZyPR4g9-nXhXlHz_bvtMvv9OKlLyogJLhV6_T3eHvh-9KapIGo_DsqD_E4y78KpOH_hAORrgEsGeMsmvRgKCsS3gFF5lso9gZuQPCSej4pS4llFwnOUmG-30QDpw1fZdjgf-5ruR07yI3uwCVugH66AhKp2MQzjqlLFVca4mDsaRWSiba8x7MHMcwBOqndUvOpKiIfRmKlJzZPgj2tn1mbhAIpkzdzh1aqfu2IiJyGXtjY6brI06MKZOJUbcYZWIw6861bWWiMO8WE9kiYgzl2MEbeg1wZ4DfO-wRxzhslXBtsqfpf_t3DX2zwiwYy14T4a17HYenvFCSpGALug3EIIL5OonJXx7lT2Mm5W1mZu30yfNkdfE8JDqgw4asp2mLSqtqC9YwWc7s63TreWlbl0cMOoCdARYx8axld1stUFx4Xz21HF-C-ptpM619XyMLuGpyTi2HWIAnQu8MiJCaQ8bLchE_GRuA4qU48cUCTV4A6ByNtClWrisyrAB8_Iy_MbBQVPq3ToApVKxVIFZiwYj4x4TCSaYL8Q3lZwrXVYe0rkHwFgP41RAGzwRiasUvy__9Aw)
## Expanded version:
```
    // Single iteration
    let f = |b: &[u8]| -> Vec<u8> {
        // Find all pairs of parenthesis and store them in v
        let v = (0..)
            .zip(b)
            .scan(vec![], |a, (b, c)| {
                Some(match c {
                    40 => {
                        a.push(b);
                        None
                    }
                    41 => Some((a.pop()?, b)),
                    _ => None,
                })
            })
            .flatten()
            .collect::<Vec<_>>();
        // For ever pair
        for k in &v {
            // If it's the first and last, return everything except the first and last character
            if k.0 == 0 && k.1 == b.len() - 1 {
                return b[1..b.len() - 1].to_vec();
            }
            for l in &v {
                //else if a second pair touches it
                if l.0 == k.0 + 1 && l.1 == k.1 - 1 {
                    // combine 3 sections to crate the whole string again without 2 parenthesis
                    return [&b[..k.0], &b[l.0..k.1], &b[k.1 + 1..]].concat();
                }
            }
        }
        return b.to_vec();
    };
    let g = |mut b: Vec<u8>| {
        //while f(x) does something
        while f(&b) != b {
            b = f(&b)
        }
        b
    };
```
]